Compare commits

...

5 Commits

Author SHA1 Message Date
inpharmaticist
faa68c9be3 change email 2026-04-05 09:27:55 -07:00
inpharmaticist
8bb2d3ff10 Update form source 2026-03-28 16:12:06 -07:00
inpharmaticist
6a93d3f731 Change contact form 2026-03-28 16:10:52 -07:00
inpharmaticist
53dd5c6077 Create contact form 2026-03-28 16:09:37 -07:00
inpharmaticist
1a3b80cddb Update index.html
update contact form
2025-12-06 18:27:13 +00:00
2 changed files with 263 additions and 2 deletions

261
contact.html Normal file
View File

@@ -0,0 +1,261 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Form</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #000;
background-color: #fff;
padding: 2rem 1rem;
max-width: 600px;
margin: 0 auto;
}
h1 {
margin-bottom: 0.5rem;
font-size: 1.8rem;
}
p {
margin-bottom: 2rem;
color: #333;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}
input, textarea {
width: 100%;
padding: 0.75rem;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
font-family: inherit;
}
input:focus, textarea:focus {
outline: none;
border-color: #000;
}
small {
display: block;
margin-top: 0.25rem;
color: #666;
font-size: 0.875rem;
}
button {
background-color: #000;
color: #fff;
padding: 1rem 2rem;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: #333;
}
button:disabled {
background-color: #999;
cursor: not-allowed;
}
#status {
margin-top: 1.5rem;
padding: 1rem;
border-radius: 4px;
display: none;
}
#status.success {
display: block;
background-color: #f0f0f0;
border-left: 4px solid #000;
}
#status.error {
display: block;
background-color: #fee;
border-left: 4px solid #c00;
color: #c00;
}
a {
color: #000;
text-decoration: underline;
}
</style>
</head>
<body>
<h1>Contact</h1>
<p>Send a message.</p>
<form id="contact-form" onsubmit="return false;">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="contact-info">Contact Info</label>
<input type="text" id="contact-info" name="contact-info"
placeholder="email@example.com" required>
<small>How you want to be contacted back.</small>
</div>
<div class="form-group">
<label for="message">Comments</label>
<textarea id="message" name="message" rows="5" placeholder="Your message..." required></textarea>
</div>
<button type="submit" id="submit-btn">Send Message</button>
</form>
<div id="status"></div>
<script src="https://unpkg.com/nostr-tools@1.17.0/lib/nostr.bundle.js"></script>
<script>
// CONFIGURATION: Replace these with your own values
const RECIPIENT_NPUB = 'npub1c0r3ytrr4afgrlhrhyec6y9wvkckdllx7ul3cfevtsgjqcrhx8tsdzqs7w';
const RELAYS = [
'wss://bits.sdbitcoiners.com/nostrrelay/Leug2TNY',
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band'
// Add more relays if desired
];
// Decode npub to hex pubkey
let RECIPIENT_HEX;
try {
const decoded = window.NostrTools.nip19.decode(RECIPIENT_NPUB);
RECIPIENT_HEX = decoded.data;
} catch (e) {
console.error('Invalid npub in configuration');
}
document.getElementById('contact-form').addEventListener('submit', async (e) => {
e.preventDefault();
const submitBtn = document.getElementById('submit-btn');
const status = document.getElementById('status');
if (!RECIPIENT_HEX) {
status.innerHTML = '<strong>Configuration error:</strong> Invalid npub.';
status.className = 'error';
return;
}
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
status.className = '';
status.style.display = 'none';
try {
const sk = window.NostrTools.generatePrivateKey();
const pk = window.NostrTools.getPublicKey(sk);
const formData = {
name: document.getElementById('name').value,
contact: document.getElementById('contact-info').value,
message: document.getElementById('message').value,
timestamp: new Date().toISOString(),
source: 'littlelink'
};
const encryptedContent = await window.NostrTools.nip04.encrypt(
sk,
RECIPIENT_HEX,
JSON.stringify(formData)
);
const event = {
kind: 4,
pubkey: pk,
created_at: Math.floor(Date.now() / 1000),
tags: [['p', RECIPIENT_HEX]],
content: encryptedContent
};
event.id = window.NostrTools.getEventHash(event);
event.sig = window.NostrTools.signEvent(event, sk);
let publishedCount = 0;
const publishPromises = RELAYS.map(relayUrl => {
return new Promise((resolve) => {
const ws = new WebSocket(relayUrl);
let timeout;
ws.onopen = () => {
ws.send(JSON.stringify(['EVENT', event]));
timeout = setTimeout(() => {
ws.close();
resolve(false);
}, 5000);
};
ws.onmessage = (msg) => {
const data = JSON.parse(msg.data);
if (data[0] === 'OK' && data[1] === event.id) {
clearTimeout(timeout);
ws.close();
resolve(true);
}
};
ws.onerror = () => {
clearTimeout(timeout);
ws.close();
resolve(false);
};
});
});
const results = await Promise.all(publishPromises);
publishedCount = results.filter(r => r).length;
if (publishedCount > 0) {
status.innerHTML = '<strong>Message sent!</strong><br>Published to ' + publishedCount + ' relay(s).';
status.className = 'success';
document.getElementById('contact-form').reset();
} else {
throw new Error('No relays accepted the message');
}
} catch (error) {
console.error(error);
status.innerHTML = '<strong>Network error.</strong><br>Please try again later.';
status.className = 'error';
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Send Message';
}
});
</script>
</body>
</html>

View File

@@ -87,10 +87,10 @@
<a class="button button-github" href="https://raw.githubusercontent.com/inpharmaticist/links/refs/heads/main/gpg.asc" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/gpg.png" alt="Email Icon">PGP Key</a> <a class="button button-github" href="https://raw.githubusercontent.com/inpharmaticist/links/refs/heads/main/gpg.asc" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/gpg.png" alt="Email Icon">PGP Key</a>
<!-- Generic Email --> <!-- Generic Email -->
<a class="button button-default" href="mailto:upstairs_evaluate832@slmail.me" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-email-alt.svg" alt="Email Icon">Email</a> <a class="button button-default" href="mailto:couch_banjo048@slmail.me" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-email-alt.svg" alt="Email Icon">Email</a>
<!-- Contact Form --> <!-- Contact Form -->
<a class="button button-default" href="https://formstr.app/#/fill/f5ff29ea0fc13932da373f91dc0030998431f3626476acc8ff8a30bd78bf2c2a" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-email.svg" alt="Email Icon">Contact Form</a> <a class="button button-default" href="contact.html" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-email.svg" alt="Email Icon">Contact Form</a>
<!-- Generic Shopping Bag --> <!-- Generic Shopping Bag -->
<a class="button button-sd-bitcoiners" href="https://pay.sdbitcoiners.com" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-shopping-bag.svg" alt="Shopping Bag Icon">Store</a> <a class="button button-sd-bitcoiners" href="https://pay.sdbitcoiners.com" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-shopping-bag.svg" alt="Shopping Bag Icon">Store</a>