From dbff19e2b9d82fc0869000f6dbe6855fc9dd1a8e Mon Sep 17 00:00:00 2001 From: Bauke Date: Wed, 23 Nov 2022 14:14:17 +0100 Subject: [PATCH] Add a content script to insert the import button into the share page. --- source/content-scripts/share/share.ts | 104 ++++++++++++++++++++++++ source/content-scripts/share/style.scss | 20 +++++ source/manifest.ts | 8 ++ 3 files changed, 132 insertions(+) create mode 100644 source/content-scripts/share/share.ts create mode 100644 source/content-scripts/share/style.scss diff --git a/source/content-scripts/share/share.ts b/source/content-scripts/share/share.ts new file mode 100644 index 0000000..94f968e --- /dev/null +++ b/source/content-scripts/share/share.ts @@ -0,0 +1,104 @@ +import './style.scss'; + +import browser from 'webextension-polyfill'; +import {Component, render} from 'preact'; +import {html} from 'htm/preact'; + +import {decodeBase64, fragmentPrefix} from '../../utilities/share-redirect.js'; +import { + RedirectParameters, + parseRedirect, + narrowMatcherType, + narrowRedirectType, +} from '../../redirect/exports.js'; +import storage from '../../redirect/storage.js'; + +type Props = Record; + +type State = { + error: string | undefined; + imported: boolean; +}; + +class ImportButton extends Component { + constructor(props: Props) { + super(props); + + this.state = { + error: undefined, + imported: false, + }; + } + + importRedirect = async () => { + const decoded = decodeBase64( + location.hash.slice(fragmentPrefix.length), + ); + + const invalidRedirectError = "This isn't a valid redirect. ☹️"; + + if ( + !narrowMatcherType(decoded.matcherType) || + !narrowRedirectType(decoded.redirectType) || + typeof decoded.matcherValue !== 'string' || + typeof decoded.redirectValue !== 'string' + ) { + this.setState({error: invalidRedirectError}); + return; + } + + const redirect = parseRedirect({ + id: -1, + enabled: true, + matcherType: decoded.matcherType, + matcherValue: decoded.matcherValue, + redirectType: decoded.redirectType, + redirectValue: decoded.redirectValue, + }); + if (redirect === undefined) { + this.setState({error: invalidRedirectError}); + return; + } + + const id = await storage.nextRedirectId(); + redirect.parameters.id = id; + await storage.save(redirect); + + this.setState({imported: true}); + }; + + render() { + const {error, imported} = this.state; + + if (error !== undefined) { + return html`

${error}

`; + } + + if (imported) { + return html` +

The redirect has been imported!

+ `; + } + + return html` + + `; + } +} + +function main() { + if (!location.hash.startsWith(fragmentPrefix)) { + return; + } + + const importRoot = document.querySelector('.re-nav-import')!; + for (const child of Array.from(importRoot.children)) { + child.remove(); + } + + render(html`<${ImportButton} />`, importRoot); +} + +main(); diff --git a/source/content-scripts/share/style.scss b/source/content-scripts/share/style.scss new file mode 100644 index 0000000..58fcae8 --- /dev/null +++ b/source/content-scripts/share/style.scss @@ -0,0 +1,20 @@ +.re-nav-import { + text-align: center; +} + +.import-button { + background-color: var(--da-4); + border: none; + color: var(--background-1); + cursor: pointer; + font-weight: bold; + padding: var(--spacing-08) var(--spacing-32); + + &:hover { + background-color: var(--foreground-1); + } +} + +.import-success { + margin-bottom: var(--spacing-16); +} diff --git a/source/manifest.ts b/source/manifest.ts index 9aa8b25..d25cad3 100644 --- a/source/manifest.ts +++ b/source/manifest.ts @@ -21,6 +21,14 @@ export default function createManifest( }, }, }, + content_scripts: [ + { + css: ['generated:content-scripts/share/style.css'], + js: ['content-scripts/share/share.ts'], + matches: ['https://holllo.org/re-nav/share/'], + run_at: 'document_end', + }, + ], }; const icons = {