├── .gitignore
├── LICENSE
├── README.md
├── firefox
├── manifest.json
└── src
├── img
├── settings.png
└── showcase.gif
├── manifest.json
├── pack.sh
└── src
├── background.js
├── background.min.js
├── content_styles.css
├── content_styles.min.css
├── icon
└── 48.png
├── inject.js
├── inject.min.js
├── lib
└── jquery-3.6.1.min.js
├── popup.html
├── popup.js
├── popup.min.js
├── popup_styles.css
└── popup_styles.min.css
/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 | NOTES.md
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 @lartsch
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > **Note**
2 | > LOOKING FOR AN ACTIVE MAINTAINER TO TAKE CARE OF THIS PROJECT!
3 | > FediAct requires some major updates/rewriting to resolve issues, increase performance/reliability and to be easier to maintain, for which I do not have time currently. Please leave a message/issue.
4 |
5 | # FediAct (v0.9.8)
6 | A Chrome/Firefox extension that simplifies follow and post interactions on Mastodon servers other than your own.
7 |
8 | **Features**:
9 | - Supports Mastodon v3 + v4 (some features v4 only)
10 | - Follow, boost, bookmark, reply, fav, vote polls and mute/block on external servers while only being logged in to your home server
11 | - Show following state and toot state (boosted, faved, bookmarked, voted) on external servers
12 | - Single click to execute action only, double click to redirect to content on home server
13 | - Reply button on external servers always redirects to home server and enters reply-mode
14 | - Hide muted content on external servers if enabled
15 | - Needs nothing more than your home server domain to work
16 |
17 | ## Navigation
18 | - [Installation](#installation)
19 | - [Setup](#setup)
20 | - [FAQ](#faq)
21 | - [Screenshots / GIFs](#screenshots--gifs)
22 | - [Manual installation](#manual-installation)
23 | - [Install in Firefox for Android](#install-in-firefox-for-android)
24 | - [Additional notes](#additional-notes)
25 | - [Todos / Planned features](#todos--planned-features)
26 | - [Contributing](#contributing)
27 |
28 | ## Installation
29 |
30 | [link-chrome]: https://chrome.google.com/webstore/detail/fediact/lmpcajpkjcclkjbliapfjfolocffednm 'Version published on Chrome Web Store'
31 | [link-firefox]: https://addons.mozilla.org/en-US/firefox/addon/fediact/ 'Version published on Mozilla Add-ons'
32 |
33 | [
][link-chrome] [
Chrome Webstore][link-chrome]
34 | All up-to-date Chromium browsers, including Kiwi and Yandex browsers on Android
35 |
36 | [
][link-firefox] [
Mozilla Addon Store][link-firefox]
37 | Up-to-date Firefox (v107+), including Firefox Nightly on Android
38 |
39 | **Please note:**
40 | - If webstore release is outdated, use the [manual installation method](#manual-installation) to install the latest version
41 | - Special installation steps for [Firefox on Android](#install-in-firefox-for-android)
42 | - Chrome store updates take 1-2 days longer
43 | - If you like this addon, please consider donating: [paypal.me/lartsch](https://paypal.me/lartsch)
44 |
45 | ## Setup
46 |
47 | 1. Make sure you are logged in to your home server
48 | 2. Click the extension icon or open its settings page
49 | 3. Set your home server domain (required)
50 | 4. Check out the other settings (optional)
51 | 5. Click the "Save" button to save
52 |
53 | If you have set your home server correctly, you can now interact on other Mastodon servers.
54 |
55 | **Please note:**
56 | - If FediAct is running, a small icon will be displayed in the bottom right corner
57 | - Also, it is indicated while content is resolving or when it could not be resolved
58 | - Performance depends on your home server and the external server you are browsing (read more [below](#additional-notes))
59 | - Some toots can't be resolved to your home (e.g. when searching the post manually wouldn't work either)
60 | - It's NOT recommended to disable the API delay (servers use rate limiting and might block your IP)
61 |
62 | ## FAQ
63 | **Why does it need permission for all websites?**
64 |
65 | > The addon needs to determine whether or not the site you are currently browsing is a Mastodon server. For that to work, it requires access to all sites. Otherwise, each existing Mastodon server would have to be explicitly added.
66 |
67 | **Can I use this on Android?**
68 |
69 | > Yes! There are three options that I am aware of: Kiwi Browser, Yandex Browser and Firefox Nightly (see [below](#install-in-firefox-for-android))
70 |
71 | **Can I use this on iOS?**
72 |
73 | > Currently not in a reliable way, but:
74 | > - It's possible that Orion Browser can soon be used (see issue [#16](https://github.com/Lartsch/FediAct/issues/16))
75 | > - There are plans for Safari support (see issue [#17](https://github.com/Lartsch/FediAct/issues/17))
76 |
77 | **Can you add feature XY?**
78 |
79 | > Feel free to create an issue here on GitHub and I will look into it.
80 |
81 | **Is this safe to use?**
82 |
83 | > This project is open source. Anyone with some programming knowledge can check out the source code, either here on GitHub or by extracting the addon file from the addon stores. You can also make improvements.
84 | > Considering the implementation, I am not aware of any risks. Efforts were made to prevent servers from abusing this addon to perform actions on the user's behalf. It does not require your username or password. All data is stored in your browser locally, with the API token being the only sensitive data. This token is **only** sent to your home server. No other data ever leaves your device. All requests are made from the background script, out-of-scope for websites you visit.
85 |
86 | ## Screenshots / GIFs
87 | v0.9.8
88 |
89 | Extension popup / settings
90 |
91 |
92 |
93 | Showcase
94 |
95 |
96 |
97 | ## Manual installation
98 | 1. Download the [latest GitHub release](https://github.com/Lartsch/FediAct/releases/latest) for your browser (Chrome or Firefox)
99 | ### Chrome
100 | 2. Unzip the downloaded file somewhere
101 | 3. Go to your Chrome extension page (URL: chrome://extensions) and enable developer mode
102 | 4. Click the "Load unpacked" button and then select the unzipped folder
103 |
104 | Note: Some Chromium browsers allow you to directly load a .zip file - you can use it if available
105 |
106 | ### Firefox
107 | 2. Open the debugging page (URL: about:debugging)
108 | 3. Select "This Firefox"
109 | 4. Click the "Load Temporary Add-on" button and then select the downloaded Firefox ZIP file
110 |
111 | ### Install in Firefox for Android
112 | For a while now, Firefox on Android has only allowed installing from a [curated list](https://addons.mozilla.org/en-US/android/search/?promoted=recommended&sort=random&type=extension) of addons, preventing installation of anything else. The following instructions will guide you through installing it from the webstore anyway.
113 |
114 | **Requirements:**
115 | - Firefox [**Nightly**](https://play.google.com/store/apps/details?id=org.mozilla.fenix) for Android
116 |
117 | **Steps:**
118 | 1. In Firefox, go to Settings > About Firefox Nightly
119 | 2. Click the Firefox logo 5 times to enable Developer options
120 | 3. Go back to Settings > Custom Add-on Collection
121 | 4. Enter the following data:
122 | - ID: 17665294
123 | - Name: FediAct
124 | 5. Click OK, Firefox will close - reopen it
125 | 6. FediAct will now be available in the Add-ons menu of Firefox Nightly
126 |
127 | To update the addon instantly, simply remove and re-install it. I don't know if or when auto-update triggers in Firefox.
128 |
129 | I included all of the default add-ons in the custom collection, so you will not miss out on any of those. Of course, you can create [your own collection](https://support.mozilla.org/en-US/kb/how-use-collections-addonsmozillaorg) as well.
130 |
131 | ## Additional notes
132 | 1. Support for other Fedi software is planned
133 | 2. There are several reasons why resolving/interacting might not work including:
134 | - Not being logged in to your home server
135 | - Element identifiers have changed / the server uses an unsupported flavour
136 | - The external server you are browsing or the originating server of a toot is not Mastodon
137 | - Your home server has strong rate limiting and limited your IP
138 | - Your home server / the external server / the original server of a toot have defederated / are moderated
139 | - The toot has not yet federated to your home instace (follow the account and toots should start federating)
140 | - The server you are browsing does not use 302 redirects for external toots
141 | - The network conditions of your home server or the external server are bad (slow speed)
142 | - That a toot is set to unlisted on its original server may play a role
143 | 3. There can be delays because API calls have to be made and it is tries to avoid error 429 (too many requests). Especially if a page has many toots or you are scrolling through a feed really fast.
144 | 4. If the extension fails to resolve content, the affected buttons will behave as if the extension weren't active (popup modal) and a notice ("Unresolved") is added to the toot
145 | 5. If "Collect errors" is enabled (Chrome), there can be uncatched errors being displayed for FediAct. This is not relevant to functionality.
146 |
147 | ## Todos / Planned features
148 | Check out the [GitHub project](https://github.com/users/Lartsch/projects/2) to see planned features and todos. They are sorted from most important to least important.
149 |
150 | ## Contributing
151 | Feel free to create [issues](https://github.com/Lartsch/FediAct/issues) for bugs and feature suggestions. Even better: Create pull requests for whatever improvements you can make! :)
152 |
153 | ## Thanks to...
154 | - @raikasdev because I stole his fix for cross-browser storage API support
155 | - @rosemarydotworld because I customized and use his awesome jQuery.DOMNodeAppear where MutationObservers and delegation failed
156 | - All the direct [contributors](https://github.com/Lartsch/FediAct/graphs/contributors) to this repository!
157 |
--------------------------------------------------------------------------------
/firefox/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "FediAct",
3 | "version": "0.9.8.7",
4 | "description": "Simplifies interactions on other Mastodon instances than your own. Visit https://github.com/lartsch/FediAct for more.",
5 | "manifest_version": 2,
6 | "content_scripts": [
7 | {
8 | "matches": [
9 | "*://*/*"
10 | ],
11 | "js": [
12 | "src/lib/jquery-3.6.1.min.js",
13 | "src/inject.min.js"
14 | ],
15 | "css": [
16 | "src/content_styles.min.css"
17 | ],
18 | "run_at": "document_start"
19 | }
20 | ],
21 | "background": {
22 | "scripts": [
23 | "src/background.min.js"
24 | ],
25 | "persistent": false
26 | },
27 | "permissions": [
28 | "storage",
29 | "alarms",
30 | "tabs",
31 | "https://*/*",
32 | "http://*/*"
33 | ],
34 | "options_ui": {
35 | "page": "src/popup.html"
36 | },
37 | "browser_action": {
38 | "default_popup": "src/popup.html",
39 | "default_icon": "src/icon/48.png",
40 | "default_title": "FediAct settings"
41 | },
42 | "icons": {
43 | "48": "src/icon/48.png"
44 | }
45 | }
--------------------------------------------------------------------------------
/firefox/src:
--------------------------------------------------------------------------------
1 | ../src
--------------------------------------------------------------------------------
/img/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lartsch/FediAct/22e3be13cbd72c77b269d68361215c8c32f42fab/img/settings.png
--------------------------------------------------------------------------------
/img/showcase.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lartsch/FediAct/22e3be13cbd72c77b269d68361215c8c32f42fab/img/showcase.gif
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "FediAct",
3 | "version": "0.9.8.7",
4 | "description": "Simplifies interactions on other Mastodon instances than your own. Visit https://github.com/lartsch/FediAct for more.",
5 | "manifest_version": 3,
6 | "content_scripts": [
7 | {
8 | "matches": [
9 | "*://*/*"
10 | ],
11 | "js": [
12 | "src/lib/jquery-3.6.1.min.js",
13 | "src/inject.min.js"
14 | ],
15 | "css": [
16 | "src/content_styles.min.css"
17 | ],
18 | "run_at": "document_start"
19 | }
20 | ],
21 | "background": {
22 | "service_worker": "src/background.min.js"
23 | },
24 | "permissions": [
25 | "storage",
26 | "alarms",
27 | "tabs"
28 | ],
29 | "host_permissions": [
30 | "https://*/*",
31 | "http://*/*"
32 | ],
33 | "options_ui": {
34 | "page": "src/popup.html"
35 | },
36 | "action": {
37 | "default_popup": "src/popup.html",
38 | "default_icon": "src/icon/48.png",
39 | "default_title": "FediAct settings"
40 | },
41 | "icons": {
42 | "48": "src/icon/48.png"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/pack.sh:
--------------------------------------------------------------------------------
1 | # RUN FROM MAIN DIR
2 | # USE ./pack.sh
3 | # NOTE: requires uglifyjs and uglifycss in PATH
4 |
5 | # remove all minified files (excluding lib folder) and zip files
6 | find . -type d -name lib -prune -o -type f -name "*.min.js" -o -type f -name "*.min.css" -o -type f -name "*.zip" | grep -v "/lib" | tr '\n' '\0' | xargs -r0 rm
7 | # minify all js files, excluding lib folder (and mangle toplevel without browser, chrome variable scopes)
8 | find . -type d -name lib -prune -o -type f -name "*.js" -exec bash -c 'uglifyjs $1 --compress -m toplevel,reserved=["chrome","browser"] -o "$(echo $1 | rev | cut -f 2- -d '.' | rev).min.js"' _ {} \;
9 | # minify all css files, excluding lib folder
10 | find . -type d -name lib -prune -o -type f -name "*.css" -exec bash -c 'uglifycss $1 > "$(echo $1 | rev | cut -f 2- -d '.' | rev).min.css"' _ {} \;
11 | # zip release files
12 | zip -r "fediact-$1-chrome.zip" ./ -x "firefox/*" -x "img/*" -x ".git/*" -x "README.md" -x "NOTES.md" -x ".gitignore" -x "pack.sh" -x "LICENSE" -x "src/background.js" -x "src/inject.js" -x "src/popup.js" -x "src/content_styles.css" -x "src/popup_styles.css" 1>/dev/null
13 | cd firefox && zip -r "../fediact-$1-firefox.zip" ./ -x "src/background.js" -x "src/inject.js" -x "src/popup.js" -x "src/content_styles.css" -x "src/popup_styles.css" 1>/dev/null
14 |
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | var browser, chrome, settings
2 | const enableConsoleLog = false
3 | const logPrepend = "[FediAct]"
4 | const tokenInterval = 1 // minutes
5 | const mutesApi = "/api/v1/mutes"
6 | const blocksApi = "/api/v1/blocks"
7 | const domainBlocksApi = "/api/v1/domain_blocks"
8 | const timeout = 15000
9 | const tokenRegex = /"access_token":".*?",/gm
10 | // required settings keys with defauls
11 | const settingsDefaults = {
12 | fediact_homeinstance: null,
13 | fediact_token: null
14 | }
15 |
16 | // wrapper to prepend to log messages
17 | function log(text) {
18 | if (enableConsoleLog) {
19 | console.log(logPrepend + ' ' + text)
20 | }
21 | }
22 |
23 | // get redirect url (it will be the url on the toot authors home instance)
24 | async function resolveExternalTootHome(url) {
25 | return new Promise(async function(resolve) {
26 | try {
27 | const controller = new AbortController()
28 | const timeoutId = setTimeout(() => {
29 | log("Timed out")
30 | controller.abort()
31 | }, timeout)
32 | var res = await fetch(url, {method: 'HEAD', signal: controller.signal})
33 | clearTimeout(timeoutId)
34 | if (res.redirected) {
35 | resolve(res.url)
36 | } else {
37 | resolve(false)
38 | }
39 | } catch(e) {
40 | log(e)
41 | resolve(false)
42 | }
43 | })
44 | }
45 |
46 | // get redirect url (it will be the url on the toot authors home instance)
47 | async function generalRequest(data) {
48 | return new Promise(async function(resolve) {
49 | try {
50 | const controller = new AbortController()
51 | const timeoutId = setTimeout(() => {
52 | log("Timed out")
53 | controller.abort()
54 | }, timeout)
55 | if (data[3]) {
56 | // json body provided, post as body to target
57 | data[2]["User-Agent"] = "FediAct Service"
58 | data[2]["Content-Type"] = "application/json"
59 | var res = await fetch(data[1], {
60 | method: data[0],
61 | signal: controller.signal,
62 | // if json body is provided, there is also header data
63 | headers: data[2],
64 | body: JSON.stringify(data[3])
65 | })
66 | } else if (data[2]) {
67 | // header data provided
68 | data[2]["User-Agent"] = "FediAct Service"
69 | var res = await fetch(data[1], {
70 | method: data[0],
71 | signal: controller.signal,
72 | headers: data[2]
73 | })
74 | } else {
75 | var res = await fetch(data[1], {
76 | method: data[0],
77 | signal: controller.signal,
78 | headers: {"User-Agent": "FediAct Service"}
79 | })
80 | }
81 | clearTimeout(timeoutId)
82 | if (res.status >= 200 && res.status < 300 ) {
83 | const contentType = res.headers.get("content-type")
84 | if (contentType && contentType.indexOf("application/json") !== -1) {
85 | var restext = await res.text()
86 | resolve(restext)
87 | } else {
88 | resolve(false)
89 | }
90 | } else {
91 | resolve(false)
92 | }
93 | } catch(e) {
94 | log(e)
95 | resolve(false)
96 | }
97 | })
98 | }
99 |
100 | // fetch API token here (will use logged in session automatically)
101 | async function fetchBearerToken() {
102 | return new Promise(async function(resolve) {
103 | var url = "https://" + settings.fediact_homeinstance
104 | try {
105 | var res = await fetch(url)
106 | var text = await res.text()
107 | } catch(e) {
108 | log(e)
109 | resolve(false)
110 | return
111 | }
112 | if (text) {
113 | // dom parser is not available in background workers, so we use regex to parse the html....
114 | // for some reason, regex groups do also not seem to work in chrome background workers... the following is ugly but should work fine
115 | var content = text.match(tokenRegex)
116 | if (content) {
117 | var indexOne = content[0].search(/"access_token":"/)
118 | var indexTwo = content[0].search(/",/)
119 | if (indexOne > -1 && indexTwo > -1) {
120 | indexOne = indexOne + 16
121 | var token = content[0].substring(indexOne, indexTwo)
122 | if (token.length > 16) {
123 | settings.fediact_token = token
124 | resolve(true)
125 | return
126 | }
127 | }
128 | }
129 | }
130 | // reset token for inject.js to know
131 | settings.fediact_token = null
132 | log("Token could not be found.")
133 | resolve(false)
134 | })
135 | }
136 |
137 | // grab all accounts/instances that are muted/blocked by the user
138 | // this is only done here in the bg script so we have data available on load of pages without first performing 3 (!) requests
139 | // otherwise this would lead to problems with element detection / low performance (espcially v3 instances)
140 | // mutes/blocks are updated in content script on page context changes and after performing mutes/block actions
141 | function fetchMutesAndBlocks() {
142 | return new Promise(async function(resolve) {
143 | try {
144 | // set empty initially
145 | [settings.fediact_mutes, settings.fediact_blocks, settings.fediact_domainblocks] = [[],[],[]]
146 | var [mutes, blocks, domainblocks] = await Promise.all([
147 | fetch("https://" + settings.fediact_homeinstance + mutesApi, {headers: {"Authorization": "Bearer "+settings.fediact_token}}).then((response) => response.json()),
148 | fetch("https://" + settings.fediact_homeinstance + blocksApi, {headers: {"Authorization": "Bearer "+settings.fediact_token}}).then((response) => response.json()),
149 | fetch("https://" + settings.fediact_homeinstance + domainBlocksApi, {headers: {"Authorization": "Bearer "+settings.fediact_token}}).then((response) => response.json())
150 | ])
151 | if (mutes.length) {
152 | settings.fediact_mutes.push(...mutes.map(acc => acc.acct))
153 | }
154 | if (blocks.length) {
155 | settings.fediact_blocks.push(...blocks.map(acc => acc.acct))
156 | }
157 | if (domainblocks.length) {
158 | settings.fediact_domainblocks = domainblocks
159 | }
160 | resolve(true)
161 | } catch {
162 | resolve(false)
163 | }
164 | })
165 | }
166 |
167 | async function fetchData(token, mutesblocks) {
168 | return new Promise(async function(resolve) {
169 | var resolved = false
170 | try {
171 | settings = await (browser || chrome).storage.local.get(settingsDefaults)
172 | if (settings.fediact_homeinstance) {
173 | if (token || mutesblocks) {
174 | if (token || !(settings.fediact_token)) {
175 | await fetchBearerToken()
176 | }
177 | if (mutesblocks) {
178 | await fetchMutesAndBlocks()
179 | }
180 | try {
181 | await (browser || chrome).storage.local.set(settings)
182 | resolved = true
183 | } catch {
184 | log(e)
185 | }
186 | }
187 | } else {
188 | log("Home instance not set")
189 | }
190 | } catch(e) {
191 | log(e)
192 | }
193 | resolve(resolved)
194 | })
195 | }
196 |
197 | async function reloadListeningScripts() {
198 | chrome.tabs.query({}, async function(tabs) {
199 | for (var i=0; i {
218 | // the content script gave us an url to perform a 302 redirect with
219 | if(request.externaltoot) {
220 | resolveExternalTootHome(request.externaltoot).then(sendResponse)
221 | return true
222 | }
223 | // the content script gave us an url to perform a 302 redirect with
224 | if(request.requestdata) {
225 | generalRequest(request.requestdata).then(sendResponse)
226 | return true
227 | }
228 | // immediately fetch api token after settings are updated
229 | if (request.updatedsettings) {
230 | fetchData(true, true).then(reloadListeningScripts)
231 | return true
232 | }
233 | if (request.updatemutedblocked) {
234 | fetchData(false, true).then(sendResponse)
235 | return true
236 | }
237 | // when the content script starts to process on a site, listen for tab changes (url)
238 | if (request.running) {
239 | chrome.tabs.onUpdated.addListener(async function(tabId, changeInfo, tab) {
240 | // chrome tabs api does not support listener filters here
241 | // if the tabId of the update event is the same like the tabId that started the listener in the first place AND when the update event is an URL
242 | if (tabId === sender.tab.id && changeInfo.url) {
243 | // ... then let the content script know about the change
244 | try {
245 | await chrome.tabs.sendMessage(tabId, {urlchanged: changeInfo.url})
246 | } catch(e) {
247 | log(e)
248 | }
249 | }
250 | })
251 | }
252 | })
--------------------------------------------------------------------------------
/src/background.min.js:
--------------------------------------------------------------------------------
1 | var browser,chrome,c;const a=!1,n="[FediAct]",t=1,i="/api/v1/mutes",s="/api/v1/blocks",o="/api/v1/domain_blocks",r=15e3,d=/"access_token":".*?",/gm,u={fediact_homeinstance:null,fediact_token:null};function h(t){a&&console.log(n+" "+t)}async function f(i){return new Promise(async function(e){try{const n=new AbortController;var t=setTimeout(()=>{h("Timed out"),n.abort()},r),a=await fetch(i,{method:"HEAD",signal:n.signal});clearTimeout(t),a.redirected?e(a.url):e(!1)}catch(t){h(t),e(!1)}})}async function l(c){return new Promise(async function(e){try{const i=new AbortController;var t,a,n=setTimeout(()=>{h("Timed out"),i.abort()},r);t=c[3]?(c[2]["User-Agent"]="FediAct Service",c[2]["Content-Type"]="application/json",await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2],body:JSON.stringify(c[3])})):c[2]?(c[2]["User-Agent"]="FediAct Service",await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2]})):await fetch(c[1],{method:c[0],signal:i.signal,headers:{"User-Agent":"FediAct Service"}}),clearTimeout(n),200<=t.status&&t.status<300&&(a=t.headers.get("content-type"))&&-1!==a.indexOf("application/json")?e(await t.text()):e(!1)}catch(t){h(t),e(!1)}})}async function m(){return new Promise(async function(e){var t="https://"+c.fediact_homeinstance;try{var a=await(await fetch(t)).text()}catch(t){return h(t),void e(!1)}if(a){t=a.match(d);if(t){var a=t[0].search(/"access_token":"/),n=t[0].search(/",/);if(-1t.json()),fetch("https://"+c.fediact_homeinstance+s,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json()),fetch("https://"+c.fediact_homeinstance+o,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json())]);e.length&&c.fediact_mutes.push(...e.map(t=>t.acct)),a.length&&c.fediact_blocks.push(...a.map(t=>t.acct)),n.length&&(c.fediact_domainblocks=n),t(!0)}catch{t(!1)}})}async function y(n,i){return new Promise(async function(t){var a=!1;try{if((c=await(browser||chrome).storage.local.get(u)).fediact_homeinstance){if(n||i){!n&&c.fediact_token||await m(),i&&await g();try{await(browser||chrome).storage.local.set(c),a=!0}catch{h(e)}}}else h("Home instance not set")}catch(t){h(t)}t(a)})}async function p(){chrome.tabs.query({},async function(t){for(var e=0;et.externaltoot?(f(t.externaltoot).then(e),!0):t.requestdata?(l(t.requestdata).then(e),!0):t.updatedsettings?(y(!0,!0).then(p),!0):t.updatemutedblocked?(y(!1,!0).then(e),!0):void(t.running&&chrome.tabs.onUpdated.addListener(async function(t,e,a){if(t===n.tab.id&&e.url)try{await chrome.tabs.sendMessage(t,{urlchanged:e.url})}catch(t){h(t)}})));
--------------------------------------------------------------------------------
/src/content_styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | Normal stylings
3 | */
4 |
5 | :root { /* Modal Vars */
6 | --bg: #eee;
7 | --fg: #494949;
8 | --border: rgba(120,120,130,0.3);
9 | --hover: rgba(100,100,130,0.2);
10 | --confirmation: rgb(38, 133, 0);
11 | }
12 |
13 | @media (prefers-color-scheme: dark) {
14 | :root {
15 | --bg: #24262d;
16 | --fg: white;
17 | }
18 | }
19 |
20 | /* Styles for the mute/block modal popup */
21 |
22 | .fediactmodal {
23 | position: fixed;
24 | z-index: 99999;
25 | left: 0;
26 | top: 0;
27 | width: 100%;
28 | height: 100%;
29 | overflow: auto;
30 | background-color: rgba(0,0,0,0.4);
31 | margin: 0;
32 | padding: 0;
33 | display: -webkit-box;
34 | display: -ms-flexbox;
35 | display: flex;
36 | font-size: 1rem;
37 | -webkit-animation: fa_fadeIn .2s;
38 | animation: fa_fadeIn .2s;
39 | }
40 |
41 | .fediactmodalinner {
42 | background-color: var(--bg);
43 | border: 1px solid var(--border);
44 | width: 80%;
45 | max-width: 300px;
46 | margin: auto;
47 | padding: .4em;
48 | border-radius: 8px;
49 | -webkit-animation: fa_scaleInSmall .2s;
50 | animation: fa_scaleInSmall .2s;
51 | }
52 | .fediactmodalitem {
53 | position: relative;
54 | display: block;
55 | padding: .7em;
56 | border-radius: 6px;
57 | cursor: pointer;
58 | -webkit-box-sizing: border-box;
59 | box-sizing: border-box;
60 | -webkit-transition: background-color .4s;
61 | -o-transition: background-color .4s;
62 | transition: background-color .4s;
63 | }
64 | .fediactmodalitem:hover, .fediactmodalitem:focus-within {
65 | background-color: var(--hover);
66 | }
67 | .fediactmodallink {
68 | display: -webkit-box;
69 | display: -ms-flexbox;
70 | display: flex;
71 | -webkit-box-align: center;
72 | -ms-flex-align: center;
73 | align-items: center;
74 | cursor: pointer;
75 | text-decoration: none;
76 | color: var(--fg);
77 | margin: -.7em;
78 | padding: .7em;
79 | border-radius: inherit;
80 | -webkit-box-shadow: inset 0 0 0 var(--confirmation);
81 | box-shadow: inset 0 0 0 var(--confirmation);
82 | -webkit-transition: padding .2s, color .2s, -webkit-box-shadow .2s cubic-bezier(.2,.2,0,1);
83 | transition: padding .2s, color .2s, -webkit-box-shadow .2s cubic-bezier(.2,.2,0,1);
84 | -o-transition: padding .2s, color .2s, box-shadow .2s cubic-bezier(.2,.2,0,1);
85 | transition: padding .2s, color .2s, box-shadow .2s cubic-bezier(.2,.2,0,1);
86 | transition: padding .2s, color .2s, box-shadow .2s cubic-bezier(.2,.2,0,1), -webkit-box-shadow .2s cubic-bezier(.2,.2,0,1);
87 | }
88 | .fediactmodallink span:not(:first-of-type) {
89 | position: absolute;
90 | right: .7em;
91 | font-size: .8em;
92 | padding: .2em .4em;
93 | border-radius: 4px;
94 | background-color: white;
95 | color: var(--confirmation);
96 | -webkit-animation: fa_scaleInFadeSmall .2s;
97 | animation: fa_scaleInFadeSmall .2s;
98 | }
99 | .fediactmodallink.activated {
100 | -webkit-box-shadow: inset 300px 0 0 var(--confirmation);
101 | box-shadow: inset 300px 0 0 var(--confirmation);
102 | color: white;
103 | font-weight: 600;
104 | }
105 |
106 | /* Styles for "resolving..." indicator */
107 | .fediactprocessing {
108 | display: inline-block;
109 | height: 1.2em;
110 | width: 1.2em;
111 | background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI0MHB4IiBoZWlnaHQ9IjQwcHgiIHZpZXdCb3g9IjAgMCA0MCA0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNDE0MjE7IiB4PSIwcHgiIHk9IjBweCI+CiAgICA8ZGVmcz4KICAgICAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWwogICAgICAgICAgICBALXdlYmtpdC1rZXlmcmFtZXMgc3BpbiB7CiAgICAgICAgICAgICAgZnJvbSB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoLTM1OWRlZykKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgQGtleWZyYW1lcyBzcGluIHsKICAgICAgICAgICAgICBmcm9tIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKC0zNTlkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHN2ZyB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybS1vcmlnaW46IDUwJSA1MCU7CiAgICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogc3BpbiAxLjVzIGxpbmVhciBpbmZpbml0ZTsKICAgICAgICAgICAgICAgIC13ZWJraXQtYmFja2ZhY2UtdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgICAgICAgICAgYW5pbWF0aW9uOiBzcGluIDEuNXMgbGluZWFyIGluZmluaXRlOwogICAgICAgICAgICB9CiAgICAgICAgXV0+PC9zdHlsZT4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJvdXRlciI+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwwQzIyLjIwNTgsMCAyMy45OTM5LDEuNzg4MTMgMjMuOTkzOSwzLjk5MzlDMjMuOTkzOSw2LjE5OTY4IDIyLjIwNTgsNy45ODc4MSAyMCw3Ljk4NzgxQzE3Ljc5NDIsNy45ODc4MSAxNi4wMDYxLDYuMTk5NjggMTYuMDA2MSwzLjk5MzlDMTYuMDA2MSwxLjc4ODEzIDE3Ljc5NDIsMCAyMCwwWiIgc3R5bGU9ImZpbGw6YmxhY2s7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44NTc4Niw1Ljg1Nzg2QzcuNDE3NTgsNC4yOTgxNSA5Ljk0NjM4LDQuMjk4MTUgMTEuNTA2MSw1Ljg1Nzg2QzEzLjA2NTgsNy40MTc1OCAxMy4wNjU4LDkuOTQ2MzggMTEuNTA2MSwxMS41MDYxQzkuOTQ2MzgsMTMuMDY1OCA3LjQxNzU4LDEzLjA2NTggNS44NTc4NiwxMS41MDYxQzQuMjk4MTUsOS45NDYzOCA0LjI5ODE1LDcuNDE3NTggNS44NTc4Niw1Ljg1Nzg2WiIgc3R5bGU9ImZpbGw6cmdiKDIxMCwyMTAsMjEwKTsiLz4KICAgICAgICA8L2c+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwzMi4wMTIyQzIyLjIwNTgsMzIuMDEyMiAyMy45OTM5LDMzLjgwMDMgMjMuOTkzOSwzNi4wMDYxQzIzLjk5MzksMzguMjExOSAyMi4yMDU4LDQwIDIwLDQwQzE3Ljc5NDIsNDAgMTYuMDA2MSwzOC4yMTE5IDE2LjAwNjEsMzYuMDA2MUMxNi4wMDYxLDMzLjgwMDMgMTcuNzk0MiwzMi4wMTIyIDIwLDMyLjAxMjJaIiBzdHlsZT0iZmlsbDpyZ2IoMTMwLDEzMCwxMzApOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksMjguNDkzOUMzMC4wNTM2LDI2LjkzNDIgMzIuNTgyNCwyNi45MzQyIDM0LjE0MjEsMjguNDkzOUMzNS43MDE5LDMwLjA1MzYgMzUuNzAxOSwzMi41ODI0IDM0LjE0MjEsMzQuMTQyMUMzMi41ODI0LDM1LjcwMTkgMzAuMDUzNiwzNS43MDE5IDI4LjQ5MzksMzQuMTQyMUMyNi45MzQyLDMyLjU4MjQgMjYuOTM0MiwzMC4wNTM2IDI4LjQ5MzksMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxMDEsMTAxLDEwMSk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMy45OTM5LDE2LjAwNjFDNi4xOTk2OCwxNi4wMDYxIDcuOTg3ODEsMTcuNzk0MiA3Ljk4NzgxLDIwQzcuOTg3ODEsMjIuMjA1OCA2LjE5OTY4LDIzLjk5MzkgMy45OTM5LDIzLjk5MzlDMS43ODgxMywyMy45OTM5IDAsMjIuMjA1OCAwLDIwQzAsMTcuNzk0MiAxLjc4ODEzLDE2LjAwNjEgMy45OTM5LDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoMTg3LDE4NywxODcpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTUuODU3ODYsMjguNDkzOUM3LjQxNzU4LDI2LjkzNDIgOS45NDYzOCwyNi45MzQyIDExLjUwNjEsMjguNDkzOUMxMy4wNjU4LDMwLjA1MzYgMTMuMDY1OCwzMi41ODI0IDExLjUwNjEsMzQuMTQyMUM5Ljk0NjM4LDM1LjcwMTkgNy40MTc1OCwzNS43MDE5IDUuODU3ODYsMzQuMTQyMUM0LjI5ODE1LDMyLjU4MjQgNC4yOTgxNSwzMC4wNTM2IDUuODU3ODYsMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxNjQsMTY0LDE2NCk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMzYuMDA2MSwxNi4wMDYxQzM4LjIxMTksMTYuMDA2MSA0MCwxNy43OTQyIDQwLDIwQzQwLDIyLjIwNTggMzguMjExOSwyMy45OTM5IDM2LjAwNjEsMjMuOTkzOUMzMy44MDAzLDIzLjk5MzkgMzIuMDEyMiwyMi4yMDU4IDMyLjAxMjIsMjBDMzIuMDEyMiwxNy43OTQyIDMzLjgwMDMsMTYuMDA2MSAzNi4wMDYxLDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoNzQsNzQsNzQpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksNS44NTc4NkMzMC4wNTM2LDQuMjk4MTUgMzIuNTgyNCw0LjI5ODE1IDM0LjE0MjEsNS44NTc4NkMzNS43MDE5LDcuNDE3NTggMzUuNzAxOSw5Ljk0NjM4IDM0LjE0MjEsMTEuNTA2MUMzMi41ODI0LDEzLjA2NTggMzAuMDUzNiwxMy4wNjU4IDI4LjQ5MzksMTEuNTA2MUMyNi45MzQyLDkuOTQ2MzggMjYuOTM0Miw3LjQxNzU4IDI4LjQ5MzksNS44NTc4NloiIHN0eWxlPSJmaWxsOnJnYig1MCw1MCw1MCk7Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K') no-repeat center center;
112 | background-size: 1em 1em;
113 | padding-left: 10px; /* for instances where action bar is not full width */
114 | padding-right: 10px;
115 | }
116 |
117 | /* Styles for unresolved indicator */
118 |
119 | .fediactunresolved {
120 | display: inline-block;
121 | height: 1.2em;
122 | width: 1.2em;
123 | color: orange;
124 | font-weight: bold;
125 | padding-right: 10px;
126 | padding-left: 10px
127 | }
128 |
129 | /* Styles for after a poll was voted */
130 |
131 | .fediactvoted {
132 | font-style: italic;
133 | }
134 |
135 | .fediactvoted > a {
136 | font-weight: bold !important;
137 | color: orange !important;
138 | }
139 |
140 | /* Inserted in the bottom right of any external instance where FediAct is running */
141 |
142 | .fediacticon {
143 | height: 32px;
144 | width: 32px;
145 | position: fixed;
146 | z-index: 99998;
147 | bottom: 50px;
148 | right: 20px;
149 | background: url('data:image/png;base64,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');
150 | background-size: 32px 32px;
151 | cursor: pointer;
152 | }
153 |
154 | .fediactsettings_onsite {
155 | position: fixed;
156 | z-index: 99998;
157 | left: 0;
158 | top: 0;
159 | width: 100%;
160 | height: 100%;
161 | overflow: auto;
162 | background-color: rgba(0,0,0,0.4);
163 | margin: 0;
164 | padding: 0;
165 | display: none;
166 | }
167 |
168 | .fediactsettings_onsite .fediactsettings_onsite_inner {
169 | position: absolute;
170 | bottom: 15px;
171 | right: 15px;
172 | padding: 10px 15px;
173 | background: white;
174 | border-radius: 5px;
175 | }
176 |
177 | .fediactsettings_onsite .fediactsettings_onsite_inner a {
178 | color: blue !important;
179 | }
180 |
181 | /* keyframes fa_*/
182 |
183 | @-webkit-keyframes fa_fadeIn {
184 | from {
185 | -webkit-filter: opacity(0);
186 | filter: opacity(0);
187 | }
188 | }
189 | @keyframes fa_fadeIn {
190 | from {
191 | -webkit-filter: opacity(0);
192 | filter: opacity(0);
193 | }
194 | }
195 | @-webkit-keyframes fa_fadeOut {
196 | to {
197 | -webkit-filter: opacity(0);
198 | filter: opacity(0);
199 | }
200 | }
201 | @keyframes fa_fadeOut {
202 | to {
203 | -webkit-filter: opacity(0);
204 | filter: opacity(0);
205 | }
206 | }
207 | @-webkit-keyframes fa_scaleInSmall {
208 | from {
209 | -webkit-transform: scale(.98);
210 | transform: scale(.98);
211 | }
212 | }
213 | @keyframes fa_scaleInSmall {
214 | from {
215 | -webkit-transform: scale(.98);
216 | transform: scale(.98);
217 | }
218 | }
219 | @-webkit-keyframes fa_scaleInFadeSmall {
220 | from {
221 | -webkit-transform: scale(.98);
222 | transform: scale(.98);
223 | -webkit-filter: opacity(0);
224 | filter: opacity(0);
225 | }
226 | }
227 | @keyframes fa_scaleInFadeSmall {
228 | from {
229 | -webkit-transform: scale(.98);
230 | transform: scale(.98);
231 | -webkit-filter: opacity(0);
232 | filter: opacity(0);
233 | }
234 | }
235 |
236 |
237 | /*
238 | We insert these styles for the DOMNodeAppeared function as separate stylesheet (see manifest) to fix
239 | Firefox blocking script-inserted