You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
12 KiB
246 lines
12 KiB
{{define "content"}} |
|
<article class="contact-page"> |
|
<h1>Contact Us</h1> |
|
|
|
<div class="contact-links" style="margin-bottom: 2rem; padding: 1rem; background: #f5f5f5; border-radius: 8px;"> |
|
<h2 style="margin-top: 0; font-size: 1.2rem;">Find us elsewhere:</h2> |
|
<ul style="list-style: none; padding: 0; margin: 0;"> |
|
<li style="margin-bottom: 0.5rem;"> |
|
<a href="https://aitherboard.imwald.eu/repos/naddr1qvzqqqrhnypzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqq9xw6t5vd5hgctyv4kqde47kt?tab=about" target="_blank" rel="noopener noreferrer" style="color: #0066cc; text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;"> |
|
<span class="icon-inline">{{icon "package"}}</span> Aitherboard Repository |
|
</a> |
|
</li> |
|
<li style="margin-bottom: 0.5rem;"> |
|
<a href="https://github.com/ShadowySupercode" target="_blank" rel="noopener noreferrer" style="color: #0066cc; text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;"> |
|
<span class="icon-inline">{{icon "github"}}</span> GitHub: ShadowySupercode |
|
</a> |
|
</li> |
|
</ul> |
|
</div> |
|
|
|
{{if .Profile}} |
|
<div class="nostr-profile" style="margin-bottom: 2rem; padding: 1rem; background: #f9f9f9; border-radius: 8px; border: 1px solid #ddd;"> |
|
<h2 style="margin-top: 0; font-size: 1.2rem;">Nostr Profile</h2> |
|
<div style="display: flex; gap: 1rem; align-items: flex-start;"> |
|
{{if .Profile.Picture}} |
|
<img src="{{.Profile.Picture}}" alt="Profile picture" style="width: 80px; height: 80px; border-radius: 50%; object-fit: cover;"> |
|
{{end}} |
|
<div style="flex: 1;"> |
|
<h3 style="margin: 0 0 0.5rem 0; font-size: 1.1rem;"> |
|
{{if .Profile.DisplayName}}{{.Profile.DisplayName}}{{else if .Profile.Name}}{{.Profile.Name}}{{else}}Anonymous{{end}} |
|
</h3> |
|
{{if .Profile.Name}} |
|
<p style="margin: 0 0 0.5rem 0; color: #666; font-size: 0.9rem;">@{{.Profile.Name}}</p> |
|
{{end}} |
|
{{if .Profile.About}} |
|
<p style="margin: 0 0 0.5rem 0; white-space: pre-wrap;">{{.Profile.About}}</p> |
|
{{end}} |
|
{{if .Profile.Website}} |
|
<p style="margin: 0.5rem 0 0 0;"> |
|
<a href="{{.Profile.Website}}" target="_blank" rel="noopener noreferrer" style="color: #0066cc; text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;"> |
|
<span class="icon-inline">{{icon "globe"}}</span> Website |
|
</a> |
|
</p> |
|
{{end}} |
|
{{if .Profile.NIP05}} |
|
<p style="margin: 0.5rem 0 0 0; color: #666; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem;"> |
|
<span class="icon-inline">{{icon "check-circle"}}</span> NIP-05: {{.Profile.NIP05}} |
|
</p> |
|
{{end}} |
|
</div> |
|
</div> |
|
</div> |
|
{{end}} |
|
|
|
<p>Have a question, suggestion, or want to report an issue? Fill out the form below and we'll get back to you.</p> |
|
|
|
{{if .Success}} |
|
<div class="alert alert-success" role="alert"> |
|
<strong><span class="icon-inline">{{icon "check-circle"}}</span> Success!</strong> Your message has been submitted. Thank you for contacting us! |
|
{{if .EventID}} |
|
<br><small><span class="icon-inline">{{icon "hash"}}</span> Issue ID: {{.EventID}}</small> |
|
{{end}} |
|
</div> |
|
{{end}} |
|
|
|
{{if .Error}} |
|
{{template "alert-error" .Error}} |
|
{{end}} |
|
|
|
<form id="contact-form" method="POST" action="/contact" class="contact-form"> |
|
<div id="nostr-status" class="alert" style="display: none;"></div> |
|
|
|
<div class="form-group"> |
|
<label for="subject">Subject <span class="required">*</span></label> |
|
<input type="text" id="subject" name="subject" required |
|
value="{{.FormData.Subject}}" |
|
placeholder="Brief description of your message" |
|
aria-required="true"> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label for="content">Message <span class="required">*</span></label> |
|
<textarea id="content" name="content" rows="10" required |
|
placeholder="Please provide details about your question, suggestion, or issue..." |
|
aria-required="true">{{.FormData.Content}}</textarea> |
|
<small class="form-help">You can use Markdown formatting in your message.</small> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label for="labels">Labels (optional)</label> |
|
<input type="text" id="labels" name="labels" |
|
value="{{.FormData.Labels}}" |
|
placeholder="bug, feature-request, question (comma-separated)"> |
|
<small class="form-help">Add labels to categorize your issue (comma-separated).</small> |
|
</div> |
|
|
|
<div class="form-actions"> |
|
<button type="submit" id="submit-btn" class="btn btn-primary"><span class="icon-inline">{{icon "send"}}</span> Submit</button> |
|
<button type="reset" class="btn btn-secondary"><span class="icon-inline">{{icon "x"}}</span> Clear</button> |
|
</div> |
|
</form> |
|
|
|
{{if .RepoAnnouncement}} |
|
<script type="application/json" id="repo-announcement-data">{{json .RepoAnnouncement}}</script> |
|
{{end}} |
|
|
|
<script> |
|
(function() { |
|
const form = document.getElementById('contact-form'); |
|
const submitBtn = document.getElementById('submit-btn'); |
|
const statusDiv = document.getElementById('nostr-status'); |
|
|
|
// Get repo announcement data from JSON script tag |
|
let repoAnnouncement = null; |
|
const repoDataEl = document.getElementById('repo-announcement-data'); |
|
if (repoDataEl) { |
|
try { |
|
repoAnnouncement = JSON.parse(repoDataEl.textContent); |
|
} catch (e) { |
|
console.error('Failed to parse repo announcement data:', e); |
|
} |
|
} |
|
|
|
function showStatus(message, isError) { |
|
statusDiv.textContent = message; |
|
statusDiv.className = 'alert ' + (isError ? 'alert-error' : 'alert-success'); |
|
statusDiv.style.display = 'block'; |
|
} |
|
|
|
function hideStatus() { |
|
statusDiv.style.display = 'none'; |
|
} |
|
|
|
form.addEventListener('submit', async function(e) { |
|
e.preventDefault(); |
|
|
|
// Check if Nostr extension is available |
|
if (!window.nostr) { |
|
showStatus('Nostr extension not found. Please install a Nostr browser extension (e.g., nos2x, Alby) to submit issues.', true); |
|
return; |
|
} |
|
|
|
if (!repoAnnouncement) { |
|
showStatus('Repository configuration not available. Please try again later.', true); |
|
return; |
|
} |
|
|
|
const subject = document.getElementById('subject').value.trim(); |
|
const content = document.getElementById('content').value.trim(); |
|
const labelsStr = document.getElementById('labels').value.trim(); |
|
|
|
if (!subject || !content) { |
|
showStatus('Subject and message are required.', true); |
|
return; |
|
} |
|
|
|
// Disable submit button |
|
submitBtn.disabled = true; |
|
submitBtn.innerHTML = '<span class="icon-inline"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg></span> Signing...'; |
|
hideStatus(); |
|
|
|
try { |
|
// Get user's public key |
|
const pubkey = await window.nostr.getPublicKey(); |
|
|
|
// Parse labels |
|
const labels = labelsStr ? labelsStr.split(',').map(l => l.trim()).filter(l => l) : []; |
|
|
|
// Build event tags |
|
const tags = []; |
|
|
|
// Add 'a' tag for repository announcement |
|
tags.push(['a', `30617:${repoAnnouncement.pubkey}:${repoAnnouncement.dTag}`]); |
|
|
|
// Add 'p' tag for repository owner |
|
tags.push(['p', repoAnnouncement.pubkey]); |
|
|
|
// Add maintainers as 'p' tags |
|
if (repoAnnouncement.maintainers && repoAnnouncement.maintainers.length > 0) { |
|
repoAnnouncement.maintainers.forEach(maintainer => { |
|
tags.push(['p', maintainer]); |
|
}); |
|
} |
|
|
|
// Add subject tag |
|
if (subject) { |
|
tags.push(['subject', subject]); |
|
} |
|
|
|
// Add label tags |
|
labels.forEach(label => { |
|
if (label) { |
|
tags.push(['t', label]); |
|
} |
|
}); |
|
|
|
// Add relays tag if available |
|
if (repoAnnouncement.relays && repoAnnouncement.relays.length > 0) { |
|
tags.push(['relays', ...repoAnnouncement.relays]); |
|
} |
|
|
|
// Create unsigned event |
|
const unsignedEvent = { |
|
kind: 1621, |
|
pubkey: pubkey, |
|
created_at: Math.floor(Date.now() / 1000), |
|
tags: tags, |
|
content: content |
|
}; |
|
|
|
// Sign the event |
|
submitBtn.innerHTML = '<span class="icon-inline"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg></span> Publishing...'; |
|
const signedEvent = await window.nostr.signEvent(unsignedEvent); |
|
|
|
// Send to API |
|
const response = await fetch('/api/contact', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ event: signedEvent }) |
|
}); |
|
|
|
const result = await response.json(); |
|
|
|
if (response.ok && result.success) { |
|
// Redirect to success page |
|
window.location.href = '/contact?success=true&event_id=' + result.event_id; |
|
} else { |
|
showStatus('Failed to publish issue: ' + (result.error || 'Unknown error'), true); |
|
submitBtn.disabled = false; |
|
submitBtn.innerHTML = '<span class="icon-inline"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.854 2.147-10.94 10.939"/></svg></span> Submit'; |
|
} |
|
} catch (error) { |
|
console.error('Error:', error); |
|
showStatus('Error: ' + error.message, true); |
|
submitBtn.disabled = false; |
|
submitBtn.innerHTML = '<span class="icon-inline"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/></svg></span> Submit'; |
|
} |
|
}); |
|
})(); |
|
</script> |
|
</article> |
|
{{end}} |
|
|
|
{{/* Feed is defined in components.html */}}
|
|
|