mirror of
https://codeberg.org/vlw/victorwesterlund.com.git
synced 2025-09-14 11:33:41 +02:00
dev21w36f-a
This commit is contained in:
parent
b05c451f11
commit
7d80341f34
7 changed files with 219 additions and 64 deletions
|
@ -13,6 +13,10 @@ body .modal.active ~ main .screen {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.active + .modal {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
/* -- Boilerplate -- */
|
/* -- Boilerplate -- */
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
|
|
|
@ -67,7 +67,9 @@
|
||||||
color: var(--swatch-accent);
|
color: var(--swatch-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
picture {
|
picture {
|
||||||
|
text-decoration: none;
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,64 @@ class Modal extends Interaction {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error(message) {
|
||||||
|
const oops = document.createElement("p");
|
||||||
|
const infoButton = document.createElement("p");
|
||||||
|
|
||||||
|
oops.classList.add("error");
|
||||||
|
oops.innerText = "🤯\nSomething went wrong";
|
||||||
|
|
||||||
|
infoButton.innerText = "more info..";
|
||||||
|
infoButton.addEventListener("click",() => {
|
||||||
|
const details = new Dialog();
|
||||||
|
|
||||||
|
details.insertHTML(`<h1>📄 Error report</h1><pre>${message}</pre>`);
|
||||||
|
details.open();
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.insertElement(infoButton);
|
||||||
|
this.insertElement(oops);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open page from "assets/pages"
|
||||||
|
openPage(page) {
|
||||||
|
// Show a spinner while fetching
|
||||||
|
const spinner = document.createElement("div");
|
||||||
|
spinner.classList = "logo spinner";
|
||||||
|
this.element.setAttribute("data-page",page);
|
||||||
|
this.insertElement(spinner);
|
||||||
|
this.open();
|
||||||
|
|
||||||
|
// Fetch the requested page
|
||||||
|
this.getPage(page).then(html => {
|
||||||
|
this.insertHTML(html);
|
||||||
|
this.bindAll(this.inner);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
const tryAgain = new Button({
|
||||||
|
text: "try again",
|
||||||
|
type: "solid"
|
||||||
|
});
|
||||||
|
tryAgain.element.addEventListener("click",() => {
|
||||||
|
// Clear and recreate modal structure
|
||||||
|
destroy(this.inner);
|
||||||
|
this.applyTemplate(this.element);
|
||||||
|
this.init();
|
||||||
|
this.insertElement(spinner);
|
||||||
|
// Attempt to fetch the requested url again (with soft rate-limiting)
|
||||||
|
setTimeout(() => {
|
||||||
|
this.openPage(page);
|
||||||
|
destroy(spinner);
|
||||||
|
},500);
|
||||||
|
});
|
||||||
|
this.insertElement(tryAgain.element);
|
||||||
|
this.error(error);
|
||||||
|
})
|
||||||
|
.finally(() => destroy(spinner));
|
||||||
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
setTimeout(() => this.element.classList.add("active"),this.transition / 2);
|
setTimeout(() => this.element.classList.add("active"),this.transition / 2);
|
||||||
}
|
}
|
||||||
|
@ -117,58 +175,4 @@ export class Card extends Modal {
|
||||||
this.bind(closeButton.element);
|
this.bind(closeButton.element);
|
||||||
this.inner.appendChild(closeButton.element);
|
this.inner.appendChild(closeButton.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message) {
|
|
||||||
const oops = document.createElement("p");
|
|
||||||
const infoButton = document.createElement("p");
|
|
||||||
|
|
||||||
oops.classList.add("error");
|
|
||||||
oops.innerText = "🤯\nSomething went wrong";
|
|
||||||
|
|
||||||
infoButton.innerText = "more info..";
|
|
||||||
infoButton.addEventListener("click",() => {
|
|
||||||
const details = new Dialog();
|
|
||||||
|
|
||||||
details.insertHTML(`<h1>📄 Error report</h1><pre>${message}</pre>`);
|
|
||||||
details.open();
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.insertElement(infoButton);
|
|
||||||
this.insertElement(oops);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open page from "assets/pages"
|
|
||||||
openPage(page) {
|
|
||||||
// Show a spinner while fetching
|
|
||||||
const spinner = document.createElement("div");
|
|
||||||
spinner.classList = "logo spinner";
|
|
||||||
this.element.setAttribute("data-page",page);
|
|
||||||
this.insertElement(spinner);
|
|
||||||
this.open();
|
|
||||||
|
|
||||||
// Fetch the requested page
|
|
||||||
this.getPage(page).then(html => {
|
|
||||||
this.insertHTML(html);
|
|
||||||
this.bindAll(this.inner);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
const tryAgain = new Button({
|
|
||||||
text: "try again",
|
|
||||||
type: "solid"
|
|
||||||
});
|
|
||||||
tryAgain.element.addEventListener("click",() => {
|
|
||||||
// Clear and recreate modal structure
|
|
||||||
destroy(this.inner);
|
|
||||||
this.applyTemplate(this.element);
|
|
||||||
this.init();
|
|
||||||
// Attempt to fetch the requested url again
|
|
||||||
this.openPage(page);
|
|
||||||
});
|
|
||||||
this.insertElement(tryAgain.element);
|
|
||||||
this.error(error);
|
|
||||||
})
|
|
||||||
.finally(() => destroy(spinner));
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright © Victor Westerlund - No libraries! 😲
|
// Copyright © Victor Westerlund - No libraries! 😲
|
||||||
import { default as Preload } from "./modules/Preload.mjs";
|
import { default as Preload } from "./modules/Preload.mjs";
|
||||||
import { default as Interaction } from "./modules/UI.mjs";
|
import { default as Interaction, destroy } from "./modules/UI.mjs";
|
||||||
import "./modules/Debugging.mjs";
|
import "./modules/Debugging.mjs";
|
||||||
|
|
||||||
// Load these assets when the DOM is ready (not needed right away)
|
// Load these assets when the DOM is ready (not needed right away)
|
||||||
|
@ -37,7 +37,7 @@ const interactions = {
|
||||||
const module = import("./modules/Modals.mjs");
|
const module = import("./modules/Modals.mjs");
|
||||||
const interactions = {
|
const interactions = {
|
||||||
getContact: (event) => {
|
getContact: (event) => {
|
||||||
const service = event.target.getAttribute("data-value");
|
const service = event.target.dataset.value;
|
||||||
module.then(modals => {
|
module.then(modals => {
|
||||||
event.target.closest(".modal").close();
|
event.target.closest(".modal").close();
|
||||||
const card = new modals.Card(interactions);
|
const card = new modals.Card(interactions);
|
||||||
|
@ -45,12 +45,21 @@ const interactions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
copyText: (event) => {
|
copyText: (event) => {
|
||||||
const memory = event.target.innerText;
|
event.target.classList.add("copied");
|
||||||
event.target.classList.add("bounce");
|
const copied = document.createElement("p");
|
||||||
event.target.innerText = "copied!";
|
copied.innerText = "copied!";
|
||||||
|
event.target.appendChild(copied);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
event.target.innerText = memory;
|
event.target.classList.remove("copied");
|
||||||
|
destroy(copied);
|
||||||
},1000);
|
},1000);
|
||||||
|
},
|
||||||
|
showPgpKey: () => {
|
||||||
|
module.then(modals => {
|
||||||
|
const dialog = new modals.Dialog();
|
||||||
|
dialog.openPage("contact_email_pgp");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,10 +67,6 @@ const interactions = {
|
||||||
const card = new modals.Card(interactions);
|
const card = new modals.Card(interactions);
|
||||||
card.openPage("contact");
|
card.openPage("contact");
|
||||||
});
|
});
|
||||||
},
|
|
||||||
openSearch: () => {
|
|
||||||
const module = import("./modules/Search.mjs");
|
|
||||||
document.body.classList.add("searchActive");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
public/assets/pages/contact_email.html
Normal file
64
public/assets/pages/contact_email.html
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<style>
|
||||||
|
.button.copied {
|
||||||
|
pointer-events: none;
|
||||||
|
animation: beat 500ms forwards;
|
||||||
|
animation-delay: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.copied svg,
|
||||||
|
.button.copied p:first-of-type {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.copied p:last-of-type {
|
||||||
|
position: absolute;
|
||||||
|
animation: slide 1000ms forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes beat {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
25% { transform: scale(.95); }
|
||||||
|
55% { transform: scale(1.05); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide {
|
||||||
|
0% { transform: translateY(calc(var(--padding))); opacity: 0; }
|
||||||
|
30% { transform: translateY(0); opacity: 1; }
|
||||||
|
70% { transform: translateY(0); opacity: 1; }
|
||||||
|
100% { transform: translateY(calc(var(--padding) * -1)); opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#interaction {
|
||||||
|
color: var(--swatch-accent);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#interaction::before {
|
||||||
|
content: "tap ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 300px) {
|
||||||
|
.button.copied p:last-of-type {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (pointer: fine) {
|
||||||
|
#interaction::before {
|
||||||
|
content: "click ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="button phantom" data-action="showPgpKey">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
|
||||||
|
<p>show PGP key</p>
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
|
<h1>hello@victorwesterlund.com</h1>
|
||||||
|
<p>You can also <a id="interaction" href="mailto:hello@victorwesterlund.com?subject=Hello Victor!">here</a> to send a mail directly from your mail app</p>
|
||||||
|
<p></p>
|
||||||
|
<div class="button solid" data-action="copyText">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18 2H9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H9V4h9v12zM3 15v-2h2v2H3zm0-5.5h2v2H3v-2zM10 20h2v2h-2v-2zm-7-1.5v-2h2v2H3zM5 22c-1.1 0-2-.9-2-2h2v2zm3.5 0h-2v-2h2v2zm5 0v-2h2c0 1.1-.9 2-2 2zM5 6v2H3c0-1.1.9-2 2-2z"/></svg>
|
||||||
|
<p>copy email</p>
|
||||||
|
</div>
|
39
public/assets/pages/contact_email_pgp.html
Normal file
39
public/assets/pages/contact_email_pgp.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<h1>🔑 PGP Public Key</h1>
|
||||||
|
<pre>
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQENBF/K6MkBCACkRMhMfYdeNP+M3XQoZHQVJgippQvYZ4QqH6F6brWD5989Xy5W
|
||||||
|
kDCvLbmPJ66boqB0dHExswOvMlhfFha65pRmfP6lIoIxZlZKwll1XASP2osS8f6r
|
||||||
|
63T7hAbL3V2Dkm49tiH1tk578xGomDrxOrd4izpH4mn9AyBIL4M+5j34bKFVZKQ+
|
||||||
|
QfMu7tduF/1oQHfDaXJeLXSfn5cNTy8DlLcLJKUSk4cjabf1D88gMVszqAAC5o1a
|
||||||
|
fI0YxoyZ+Fv+CmyrQm2iIZ3+MyDU9JAvoImtlp1h5aNgbFRDi2vKcSlv158Hq97Z
|
||||||
|
XlH1ttRZuFZiJzb8iukgUUFi4RORoXWt2rtNABEBAAG0LlZpY3RvciBXZXN0ZXJs
|
||||||
|
dW5kIDxoZWxsb0B2aWN0b3J3ZXN0ZXJsdW5kLmNvbT6JATUEEAEIACkFAl/K6MkG
|
||||||
|
CwkIBwMCCRBb4MsL47tp2gQVCAIKAxYCAQIZAQIbAwIeAQAAJ5MIAKDl9yHjwTO7
|
||||||
|
20sDrPa6ECsSBU/FwkvkWecuauvY19/OqtacNk8dEeiITLeUeBXkvNzN+P0y8hoF
|
||||||
|
ABZeir59dsY00iIp8gm03eLalhcblR5jYe3c08HssJH8PksczP3kitRNLvPAf2nU
|
||||||
|
BYg3zca5Ka21/4BPRLFb9SAQGxfHyZdy3Poug+o+pokbeK2wLqqfSMtH+waBB6Lg
|
||||||
|
2dRXuEnaZorUpNBpsahxastvNehv31Ke41Brvft15VKpO25GKZDPhm0odXMth1/J
|
||||||
|
pzWRQtndazY2guB0Ft+5wujv28HFCgVgZn2fKiQVytAetO+/wzPijBkGRvdIE+Zb
|
||||||
|
VRd3Nc0mHI65AQ0EX8royQEIALcoWEurmyXD2LoGvR+sYW+YPAPM6KG8KF4cWUn8
|
||||||
|
8+kZ6F4FH9OW64di2npYe3x+zR7DgQ1yHXcmalAsP0nN4JWTavLwsSO+JAv8NpL5
|
||||||
|
bgDs6fGaEQFl+X4fYOpkBkBmb1JrbnBk1a2u3qsEw8t7+wW1LG9z/Si5+G1KQko8
|
||||||
|
x/PEaZ2ZVv7L51ZfIQRnMtl4vL5X23BPVsDywotvuFqlTiSjGP4CR0lVa5CRv3DJ
|
||||||
|
FSmHxAxeI0vMMlwbIIUTrtwJR320sZvh2cRiwAXHQXm6l0ojzRnl46mmXnB3N6q9
|
||||||
|
PyWOaUgPrMFjT24wtgopIOwbFAT3xTr1Un0FbdeaG9JhdJ8AEQEAAYkBHwQYAQgA
|
||||||
|
EwUCX8royQkQW+DLC+O7adoCGwwAAIV/B/9OLYeQOxbXh1/hvW7/oTvN1py8wfFq
|
||||||
|
buvQSrb/MZKm6lZgG+kQy3DWjGTi/xvNqDHfBiObFSGso8RHSbHFldzEuMgrgoWW
|
||||||
|
/4JH1GDiKOp+rmBxfG30/DzOoFSfVcUfP5r8xNQby4Bh6zJhKPKVB3sZjO8cHNZD
|
||||||
|
HcNAqT3Gh5yFzsUna+ZjvPF7iU5RF1YP46dsIdvuo4xFbHpEPoZs7wgZijf+vmKO
|
||||||
|
lP61UFvKuXzwcLiI6s919EBJ9+7je8ZAxe6BCaazk+AhxXeokVvDgwQ150DNk4up
|
||||||
|
1ftWZI0LHqEpVGNejQ09uu+TdC/ISy/Ti0XKlJDER1eUL577YRUl876Y
|
||||||
|
=2qWm
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----</pre>
|
||||||
|
<h2>5466 B1EB 2F44 6D3D DC34 E9F7 5BE0 CB0B E3BB 69DA</h2>
|
||||||
|
<a href="https://storage.googleapis.com/public.victorwesterlund.com/publickey.gpg"><div class="button solid">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z"/></svg>
|
||||||
|
<p>download .gpg</p>
|
||||||
|
</div></a>
|
|
@ -29,10 +29,47 @@
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.copied {
|
||||||
|
pointer-events: none;
|
||||||
|
animation: beat 500ms forwards;
|
||||||
|
animation-delay: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.copied svg,
|
||||||
|
.button.copied p:first-of-type {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.copied p:last-of-type {
|
||||||
|
position: absolute;
|
||||||
|
animation: slide 1000ms forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes beat {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
25% { transform: scale(.95); }
|
||||||
|
55% { transform: scale(1.05); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide {
|
||||||
|
0% { transform: translateY(calc(var(--padding))); opacity: 0; }
|
||||||
|
30% { transform: translateY(0); opacity: 1; }
|
||||||
|
70% { transform: translateY(0); opacity: 1; }
|
||||||
|
100% { transform: translateY(calc(var(--padding) * -1)); opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 300px) {
|
||||||
|
.button.copied p:last-of-type {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<img id="logo_signal" src="assets/img/icons/signal.svg"/>
|
<img id="logo_signal" src="assets/img/icons/signal.svg"/>
|
||||||
<h1 id="number">+4670-2452459</h1>
|
<h1 id="number">+4670-2452459</h1>
|
||||||
<p>Signal is a free and encrypted message platform with apps for all major platforms.</p>
|
<p>Signal is a free and encrypted message platform with apps for all major platforms.</p>
|
||||||
<div class="button solid" data-action="copyText">
|
<div class="button solid" data-action="copyText">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18 2H9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H9V4h9v12zM3 15v-2h2v2H3zm0-5.5h2v2H3v-2zM10 20h2v2h-2v-2zm-7-1.5v-2h2v2H3zM5 22c-1.1 0-2-.9-2-2h2v2zm3.5 0h-2v-2h2v2zm5 0v-2h2c0 1.1-.9 2-2 2zM5 6v2H3c0-1.1.9-2 2-2z"/></svg>
|
||||||
<p>copy number</p>
|
<p>copy number</p>
|
||||||
</div>
|
</div>
|
Loading…
Add table
Reference in a new issue