Styled landing, Bluesky and Fedi redirect pages.

This commit is contained in:
flash 2025-02-27 00:16:46 +00:00
parent 7e75a71bd2
commit b6f5a4e0cc
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
24 changed files with 406 additions and 132 deletions
assets
common.js
misuzu.css
redir-bsky.js
redir-fedi.js
redirects.css
redirects.js

View file

@ -18,10 +18,13 @@ const MszLoadingIcon = function() {
let tsLastUpdate;
let counter = 0;
let playing = false;
let delay = 50;
let playResolve;
let pauseResolve;
const update = tsCurrent => {
try {
if(tsLastUpdate !== undefined && (tsCurrent - tsLastUpdate) < 50)
if(tsLastUpdate !== undefined && (tsCurrent - tsLastUpdate) < delay)
return;
tsLastUpdate = tsCurrent;
@ -30,29 +33,94 @@ const MszLoadingIcon = function() {
++counter;
} finally {
if(playResolve)
try {
playResolve();
} finally {
playResolve = undefined;
playing = true;
}
if(pauseResolve)
try {
pauseResolve();
} finally {
pauseResolve = undefined;
playing = false;
}
if(playing)
requestAnimationFrame(update);
}
};
const play = () => {
if(playing)
return;
playing = true;
requestAnimationFrame(update);
return new Promise(resolve => {
if(playing || playResolve) {
resolve();
return;
}
playResolve = resolve;
requestAnimationFrame(update);
});
};
const pause = () => {
return new Promise(resolve => {
if(!playing || pauseResolve) {
resolve();
return;
}
pauseResolve = resolve;
});
};
const stop = async () => {
await pause();
counter = 0;
};
const restart = async () => {
await stop();
await play();
};
const reverse = () => {
blocks.reverse();
};
const setBlock = (num, state=null) => {
element.children[num].classList.toggle('msz-loading-icon-block-hidden', !state);
};
const batsu = () => {
setBlock(0, true);setBlock(1, false);setBlock(2, true);
setBlock(3, false);setBlock(4, true);setBlock(5, false);
setBlock(6, true);setBlock(7, false);setBlock(8, true);
};
const maru = () => {
setBlock(0, true);setBlock(1, true);setBlock(2, true);
setBlock(3, true);setBlock(4, false);setBlock(5, true);
setBlock(6, true);setBlock(7, true);setBlock(8, true);
};
const pause = () => { playing = false; };
const stop = () => { pause(); counter = 0; };
const restart = () => { stop(); play(); };
return {
get element() { return element; },
get playing() { return playing; },
get delay() { return delay; },
set delay(value) {
if(typeof value !== 'number')
value = parseFloat(value);
if(isNaN(value) || !isFinite(value))
return;
if(value < 0)
value = Math.abs(value);
delay = value;
},
play: play,
pause: pause,
stop: stop,
restart: restart,
play,
pause,
stop,
restart,
reverse,
batsu,
maru,
};
};

View file

@ -30,7 +30,6 @@
width: 100%;
height: 100%;
mask-image: linear-gradient(0deg, transparent 10%, var(--background-colour) 100%);
-webkit-mask-image: linear-gradient(0deg, transparent 10%, var(--background-colour) 100%);
background: var(--background-pattern);
background-color: var(--accent-colour);
background-blend-mode: multiply;

View file

@ -1,25 +0,0 @@
(async () => {
const status = document.querySelector('.js-status');
status.textContent = 'Looking up DID...';
let did = null;
try {
const response = await fetch(`${location.protocol}//${BSKY_HANDLE}/.well-known/atproto-did`);
did = await response.text();
} catch(ex) {
status.style.color = 'red';
status.textContent = `Could not find DID! ${ex}`;
return;
}
if(typeof did !== 'string' || !did.startsWith('did:')) {
status.style.color = 'red';
status.textContent = 'Look up result was not a valid DID.';
return;
}
const url = BSKY_FORMAT.replace('%s', did);
status.textContent = `Redirecting to ${url}...`;
location.replace(url);
})();

View file

@ -1,39 +0,0 @@
(async () => {
const status = document.querySelector('.js-status');
status.textContent = 'Looking up Fediverse profile...';
let url = null;
try {
const response = await fetch(`${location.protocol}//${FEDI_INSTANCE}/.well-known/webfinger?format=json&resource=acct:${FEDI_USERNAME}@${FEDI_INSTANCE}`);
const webfinger = await response.json();
if(typeof webfinger === 'object') {
if(Array.isArray(webfinger.links))
for(const link of webfinger.links)
if(typeof link === 'object' && link.rel === 'http://webfinger.net/rel/profile-page' && typeof link.href === 'string') {
url = link.href;
break;
}
if(typeof url !== 'string' && Array.isArray(webfinger.aliases))
for(const alias of webfinger.aliases)
if(typeof alias === 'string') {
url = alias;
break;
}
}
} catch(ex) {
status.style.color = 'red';
status.textContent = `Could not complete Webfinger lookup! ${ex}`;
return;
}
if(typeof url !== 'string' || (!url.startsWith('https://') && !url.startsWith('http://'))) {
status.style.color = 'red';
status.textContent = 'Could not find an acceptable profile URL.';
return;
}
status.textContent = `Redirecting to ${url}...`;
location.replace(url);
})();

View file

@ -0,0 +1,42 @@
.redir-landing {
display: flex;
flex: 1 0 auto;
padding: 10px;
width: 100%;
align-items: center;
justify-content: center;
}
.redir-landing-content {
max-width: 500px;
width: 100%;
background: #191919;
box-shadow: 0 1px 2px #0009;
display: flex;
flex-direction: column;
}
.redir-landing-body {
text-align: center;
}
.redir-landing-body p {
margin: .5em;
}
.redir-landing-footer {
font-size: .8em;
line-height: 1.4em;
text-align: center;
margin: 10px;
}
.redir-landing-logo {
display: inline-block;
vertical-align: middle;
background-color: #fff;
mask: url('/images/flashii.svg') no-repeat center;
width: 140px;
height: 140px;
margin: 10px;
font-size: 0;
}

View file

@ -0,0 +1,54 @@
:root {
--accent-colour: #8559a5;
}
body {
width: 100%;
height: 100%;
background-color: #111;
color: #fff;
font-size: 16px;
line-height: 25px;
font-family: var(--font-regular);
overflow-y: scroll;
position: static;
display: flex;
}
pre, code {
font-family: var(--font-monospace);
}
a {
color: #1e90ff;
text-decoration: none;
}
a:visited {
color: #6B4F80;
}
a:hover,
a:focus {
text-decoration: underline;
}
.redir-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
mask-image: linear-gradient(315deg, #000f 0, #0000 40%);
background: url('/images/clouds.png');
background-color: var(--accent-colour);
background-blend-mode: multiply;
}
.redir-foreground {
display: flex;
flex-direction: column;
flex: 1 0 auto;
margin-bottom: 10px;
}
@include landing.css;
@include social.css;

View file

@ -0,0 +1,26 @@
.redir-social {
display: flex;
flex: 1 0 auto;
padding: 10px;
width: 100%;
align-items: center;
justify-content: center;
}
.redir-social-content {
max-width: 500px;
width: 100%;
background: #191919;
box-shadow: 0 1px 2px #0009;
display: flex;
flex-direction: column;
padding: 1em;
}
.redir-social-body {
text-align: center;
}
.redir-social-body p {
margin: .5em;
line-height: 1.4em;
}

View file

@ -0,0 +1,32 @@
const MszRedirectsBsky = async () => {
const loading = new MszLoading({ element: '.js-loading', size: 2, inline: true });
const statusBig = document.querySelector('.js-status-big');
const statusSmall = document.querySelector('.js-status-small');
const setStatusSmall = body => {
$removeChildren(statusSmall);
$appendChild(statusSmall, body);
};
setStatusSmall(<>Resolving ATProto DID for @{BSKY_HANDLE}...</>);
try {
const { body } = await $xhr.get(`${location.protocol}//${BSKY_HANDLE}/.well-known/atproto-did`);
if(typeof body !== 'string' || !body.startsWith('did:'))
throw new Error('Was unable to resolve the given handle.');
const url = BSKY_FORMAT.replace('%s', body);
setStatusSmall(<>Redirecting to <a href={url} rel="noopener">{url}</a>...</>);
location.replace(url);
} catch(ex) {
await loading.icon.stop();
loading.icon.batsu();
let errorText = ex.toString();
if(errorText.startsWith('['))
errorText = 'Something went wrong.';
statusBig.textContent = 'Profile not found!';
setStatusSmall(<span style="color: red">{errorText}</span>);
}
};

View file

@ -0,0 +1,49 @@
const MszRedirectsFedi = async () => {
const loading = new MszLoading({ element: '.js-loading', size: 2, inline: true });
const statusBig = document.querySelector('.js-status-big');
const statusSmall = document.querySelector('.js-status-small');
const setStatusSmall = body => {
$removeChildren(statusSmall);
$appendChild(statusSmall, body);
};
setStatusSmall(<>Resolving Webfinger resource for @{FEDI_USERNAME}@{FEDI_INSTANCE}...</>);
try {
const { body } = await $xhr.get(`${location.protocol}//${FEDI_INSTANCE}/.well-known/webfinger?format=json&resource=acct:${FEDI_USERNAME}@${FEDI_INSTANCE}`, { type: 'json' });
let url;
if(typeof body === 'object') {
if(Array.isArray(body.links))
for(const link of body.links)
if(typeof link === 'object' && link.rel === 'http://webfinger.net/rel/profile-page' && typeof link.href === 'string') {
url = link.href;
break;
}
if(typeof url !== 'string' && Array.isArray(body.aliases))
for(const alias of body.aliases)
if(typeof alias === 'string') {
url = alias;
break;
}
}
if(typeof url !== 'string' || (!url.startsWith('https://') && !url.startsWith('http://')))
throw new Error('Unable to resolve profile URL for given handle.');
setStatusSmall(<>Redirecting to <a href={url} rel="noopener">{url}</a>...</>);
location.replace(url);
} catch(ex) {
await loading.icon.stop();
loading.icon.batsu();
let errorText = ex.toString();
if(errorText.startsWith('['))
errorText = 'Something went wrong.';
statusBig.textContent = 'Profile not found!';
setStatusSmall(<span style="color: red">{errorText}</span>);
}
};

View file

@ -0,0 +1,9 @@
#include bsky.jsx
#include fedi.jsx
(() => {
if(location.pathname === '/bsky' || location.pathname.startsWith('/bsky/'))
MszRedirectsBsky();
if(location.pathname === '/fedi' || location.pathname.startsWith('/fedi/'))
MszRedirectsFedi();
})();