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.
 
 
 
 
 

195 lines
7.7 KiB

{{define "content"}}
<article class="contact-page">
<h1>Contact Us</h1>
<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>Success!</strong> Your message has been submitted. Thank you for contacting us!
{{if .EventID}}
<br><small>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">Submit</button>
<button type="reset" class="btn btn-secondary">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.textContent = '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.textContent = '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.textContent = 'Submit';
}
} catch (error) {
console.error('Error:', error);
showStatus('Error: ' + error.message, true);
submitBtn.disabled = false;
submitBtn.textContent = 'Submit';
}
});
})();
</script>
</article>
{{end}}
{{/* Feed is defined in components.html */}}