Add the Re-Nav share page.
This commit is contained in:
parent
4d0fd3e60d
commit
7b11e66551
|
@ -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">
|
||||||
|
©
|
||||||
|
<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 %}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -13,6 +13,21 @@ declare global {
|
||||||
readonly PROD: boolean;
|
readonly PROD: boolean;
|
||||||
readonly VITE_BROWSER: 'chromium' | 'firefox';
|
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.
|
// Make TypeScript see this file as a module.
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default defineConfig({
|
||||||
home: path.join(sourceDir, 'index.html'),
|
home: path.join(sourceDir, 'index.html'),
|
||||||
queue: path.join(sourceDir, 'queue/index.html'),
|
queue: path.join(sourceDir, 'queue/index.html'),
|
||||||
're-nav': path.join(sourceDir, 're-nav/index.html'),
|
're-nav': path.join(sourceDir, 're-nav/index.html'),
|
||||||
|
're-nav/share': path.join(sourceDir, 're-nav/share/index.html'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
|
Loading…
Reference in New Issue