1
Fork 0

Add the Re-Nav share page.

This commit is contained in:
Bauke 2022-11-22 14:17:30 +01:00
parent 4d0fd3e60d
commit 7b11e66551
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
5 changed files with 209 additions and 0 deletions

View File

@ -0,0 +1,61 @@
{% extends "source/includes/base.html" %}
{% import "source/includes/macros.html" as macros %}
{% set title = "Re-Nav ↩ Share" %}
{% block head %}
<link rel="stylesheet" href="/scss/re-nav/re-nav.scss">
<link rel="stylesheet" href="/scss/re-nav/share.scss">
{% endblock %}
{% block body %}
<header class="page-header">
<h1>
<img src="../../assets/re-nav.png" alt="Re-Nav Logo">
Re-Nav
</h1>
</header>
<main class="page-main">
<p class="subtitle">
Welcome to Re-Nav's share page!
<br />
Use the share button in Re-Nav's options page to create a link with your
redirect.
</p>
<div class="hidden-by-default redirect">
<p class="matcher-data">
<span class="matcher-type"></span>
{{-":"-}}
<span class="matcher-value"></span>
</p>
<p class="arrow"></p>
<p class="redirect-data">
<span class="redirect-type"></span>
{{-":"-}}
<span class="redirect-value"></span>
</p>
</div>
{#
This element will be shown by JS on page load, and if Re-Nav is installed,
it will be used as the root container for the import button.
#}
<div class="hidden-by-default re-nav-import">
<p>
With <a href="..">Re-Nav</a> installed, you'll see a button here to import
this redirect.
</p>
</div>
</main>
<footer class="page-footer">
&copy;
<a href="https://git.bauke.xyz/Holllo/re-nav">AGPL-3.0-or-later</a>
💖
<a href="mailto:helllo@holllo.org">helllo@holllo.org</a>
</footer>
<script src="../../ts/re-nav/share.ts" type="module"></script>
{% endblock %}

View File

@ -0,0 +1,54 @@
.page-main {
display: grid;
gap: var(--spacing-16);
grid-template-columns: repeat(1, 1fr);
}
.subtitle {
font-weight: bold;
text-align: center;
}
.redirect {
border: 1px solid var(--foreground-1);
display: grid;
gap: var(--spacing-08);
grid-template-columns: repeat(1, 1fr);
padding: var(--spacing-16);
.matcher-data,
.redirect-data {
background-color: var(--background-color);
color: var(--background-1);
padding: var(--spacing-16);
overflow: auto;
text-align: center;
span {
font-weight: bold;
}
}
.matcher-data {
--background-color: var(--da-3);
}
.redirect-data {
--background-color: var(--da-7);
}
.arrow {
font-weight: bold;
text-align: center;
}
}
.re-nav-import {
p {
text-align: center;
}
}
.hidden-by-default {
display: none;
}

78
source/ts/re-nav/share.ts Normal file
View File

@ -0,0 +1,78 @@
import {Base64} from 'js-base64';
const fragmentPrefix = '#json=';
const exampleRedirect: Redirect = {
matcherType: 'hostname',
matcherValue: 'example.com',
redirectType: 'hostname',
redirectValue: 'example.org',
};
function decodeBase64<T>(base64: string): T {
return JSON.parse(Base64.decode(base64)) as T;
}
function encodeBase64(source: any): string {
return Base64.encode(JSON.stringify(source), true);
}
window.addEventListener('DOMContentLoaded', () => {
window['Re-Nav'] = {
decodeBase64,
encodeBase64,
share,
};
console.log(
"Want to manually create a share link? Use the window['Re-Nav'].share",
'function, where redirect is an object with matcherType, matcherValue,',
'redirectType and redirectValue properties. Like this:',
);
console.log(JSON.stringify(exampleRedirect, undefined, 2));
console.log(window['Re-Nav'].share);
if (!location.hash.startsWith(fragmentPrefix)) {
return;
}
const decoded = decodeBase64<Redirect>(
location.hash.slice(fragmentPrefix.length),
);
for (const [key, value] of Object.entries(decoded)) {
insertText(key as keyof Redirect, value);
}
for (const element of document.querySelectorAll('.hidden-by-default')) {
element.classList.remove('hidden-by-default');
}
document.querySelector('.subtitle')!.textContent =
'Someone shared a redirect with you!';
});
function insertText(key: keyof Redirect, value: string): void {
const insert = (selector: string, text: string) => {
document.querySelector(selector)!.textContent = text;
};
// eslint-disable-next-line unicorn/prefer-switch
if (key === 'matcherType') {
insert('.matcher-type', value);
} else if (key === 'matcherValue') {
insert('.matcher-value', value);
} else if (key === 'redirectType') {
insert('.redirect-type', value);
} else if (key === 'redirectValue') {
insert('.redirect-value', value);
} else {
console.warn(`Unknown key: ${key as string}`);
}
}
function share(redirect: Redirect): string {
const encoded = encodeBase64(redirect);
const url = new URL(window.location.href);
url.hash = `${fragmentPrefix}${encoded}`;
return url.href;
}

15
source/types.d.ts vendored
View File

@ -13,6 +13,21 @@ declare global {
readonly PROD: boolean;
readonly VITE_BROWSER: 'chromium' | 'firefox';
}
interface Window {
'Re-Nav': {
decodeBase64<T>(base64: string): T;
encodeBase64(source: any): string;
share(redirect: Redirect): string;
};
}
type Redirect = {
matcherType: string;
matcherValue: string;
redirectType: string;
redirectValue: string;
};
}
// Make TypeScript see this file as a module.

View File

@ -19,6 +19,7 @@ export default defineConfig({
home: path.join(sourceDir, 'index.html'),
queue: path.join(sourceDir, 'queue/index.html'),
're-nav': path.join(sourceDir, 're-nav/index.html'),
're-nav/share': path.join(sourceDir, 're-nav/share/index.html'),
},
},
sourcemap: true,