Files
bitvid/components/iframe_forms/iframe-feedback-form.html
T

369 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>bitvid General Feedback Form</title>
<script type="module" src="./theme-sync.js"></script>
<!-- Link to your main stylesheet -->
<link rel="stylesheet" href="../../css/tailwind.generated.css" />
<!-- Load nostr‑tools v2.17.0 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/nostr.bundle.min.js"></script>
</head>
<body class="iframe-form" data-ds="new">
<div class="container">
<div class="form-container">
<p>
Your feedback helps us improve bitvid! Whether it’s a suggestion, a
concern, or general thoughts on the platform, we’d love to hear from
you.
</p>
<form id="feedback-form">
<!-- Section 1: User Information -->
<h2>1. User Information</h2>
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
<input
class="form-control"
type="text"
id="userNpub"
placeholder="Enter your npub (optional)"
/>
<p>Are you a (check all that apply):</p>
<div class="checkbox-group">
<label
><input
class="checkbox"
type="checkbox"
name="userRole"
value="Viewer"
/>
Viewer</label
>
<label
><input
class="checkbox"
type="checkbox"
name="userRole"
value="Content Creator"
/>
Content Creator</label
>
<label
><input
class="checkbox"
type="checkbox"
name="userRole"
value="Developer/Contributor"
/>
Developer / Contributor</label
>
</div>
<!-- Section 2: General Feedback -->
<h2>2. General Feedback</h2>
<label>How would you rate your experience on bitvid so far?</label>
<div class="radio-group">
<label
><input type="radio" name="experienceRating" value="Excellent" />
Excellent</label
>
<label
><input type="radio" name="experienceRating" value="Good" />
Good</label
>
<label
><input type="radio" name="experienceRating" value="Average" />
Average</label
>
<label
><input
type="radio"
name="experienceRating"
value="Needs Improvement"
/>
Needs Improvement</label
>
</div>
<label for="likeMost">What do you like most about bitvid?</label>
<textarea
class="textarea"
id="likeMost"
rows="3"
placeholder="Describe the features, usability, or content you enjoy"
></textarea>
<label for="improvements">What would you like to see improved?</label>
<textarea
class="textarea"
id="improvements"
rows="3"
placeholder="Provide specific areas for improvement"
></textarea>
<label for="confusingFeatures"
>Are there any features or tools you find confusing or difficult to
use?</label
>
<textarea
class="textarea"
id="confusingFeatures"
rows="3"
placeholder="Explain any challenges you’ve encountered"
></textarea>
<!-- Section 3: Additional Comments -->
<h2>3. Additional Comments</h2>
<label for="otherSuggestions"
>Do you have any other suggestions or thoughts about bitvid?</label
>
<textarea
class="textarea"
id="otherSuggestions"
rows="3"
placeholder="Share any other feedback"
></textarea>
<label for="followUp"
>Would you like to be contacted for follow-up discussions?</label
>
<select class="select" id="followUp">
<option value="">Select an option</option>
<option value="Yes">Yes</option>
<option value="No">No</option>
</select>
<label for="preferredContact"
>Preferred contact method (if applicable):</label
>
<input
class="form-control"
type="text"
id="preferredContact"
placeholder="Nostr DM, email, or other"
/>
<button class="btn" type="submit">Submit General Feedback</button>
</form>
<div id="status"></div>
</div>
</div>
<script>
// Provide no-op fallback loggers so embeds stay functional if bitvidLogger is unavailable.
const noopLogger = {
log: () => {},
info: () => {},
warn: () => {},
error: () => {},
};
const dev = window.bitvidLogger?.dev || noopLogger;
const user = window.bitvidLogger?.user || noopLogger;
document.addEventListener("DOMContentLoaded", () => {
// Logging function for on-page and console output.
function log(msg, type = "info") {
const div = document.createElement("div");
div.classList.add("status-line");
if (type === "error") div.classList.add("error");
if (type === "success") div.classList.add("success");
if (type === "warn") div.classList.add("warn");
div.textContent = msg;
document.getElementById("status").appendChild(div);
const formatted = `[${type.toUpperCase()}] ${msg}`;
if (type === "error") {
user.error(formatted);
} else if (type === "warn") {
user.warn(formatted);
} else if (type === "success") {
user.log(formatted);
} else if (type === "info") {
dev.info(formatted);
} else {
dev.log(formatted);
}
}
function clear() {
document.getElementById("status").innerHTML = "";
}
if (!window.NostrTools) {
log("NostrTools not loaded. Check console or ad-blockers.", "error");
return;
}
const {
generateSecretKey,
getPublicKey,
finalizeEvent,
nip04,
nip19,
SimplePool,
Relay,
} = window.NostrTools;
// Set the recipient's NPUB (your personal NPUB)
const recipientNpub =
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
const RELAYS = [
"wss://relay.snort.social",
"wss://relay.damus.io",
"wss://relay.primal.net",
];
const pool = new SimplePool();
document
.getElementById("feedback-form")
.addEventListener("submit", async (ev) => {
ev.preventDefault();
clear();
try {
// Section 1: User Information
const userNpub = document.getElementById("userNpub").value.trim();
const roleNodes = document.querySelectorAll(
'input[name="userRole"]:checked'
);
let userRoles = [];
roleNodes.forEach((node) => {
userRoles.push(node.value);
});
// Section 2: General Feedback
const experienceRadio = document.querySelector(
'input[name="experienceRating"]:checked'
);
const experienceRating = experienceRadio
? experienceRadio.value
: "N/A";
const likeMost = document.getElementById("likeMost").value.trim();
const improvements = document
.getElementById("improvements")
.value.trim();
const confusingFeatures = document
.getElementById("confusingFeatures")
.value.trim();
// Section 3: Additional Comments
const otherSuggestions = document
.getElementById("otherSuggestions")
.value.trim();
const followUp = document.getElementById("followUp").value.trim();
const preferredContact = document
.getElementById("preferredContact")
.value.trim();
// Construct the Markdown feedback content
const feedbackContent = `
# **bitvid General Feedback Form**
Your feedback helps us improve bitvid! Whether it’s a suggestion, a concern, or general thoughts on the platform, we’d love to hear from you.
## **1. User Information**
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
- **Are you a (check all that apply):** ${
userRoles.length > 0 ? userRoles.join(", ") : "N/A"
}
## **2. General Feedback**
- **How would you rate your experience on bitvid so far?** ${experienceRating}
- **What do you like most about bitvid?**
${likeMost || "N/A"}
- **What would you like to see improved?**
${improvements || "N/A"}
- **Are there any features or tools you find confusing or difficult to use?**
${confusingFeatures || "N/A"}
## **3. Additional Comments**
- **Do you have any other suggestions or thoughts about bitvid?**
${otherSuggestions || "N/A"}
- **Would you like to be contacted for follow-up discussions?** ${
followUp || "N/A"
}
- **Preferred contact method (if applicable):** ${preferredContact || "N/A"}
---
### **Processing & Consideration**
We review all feedback regularly to improve bitvid. While not all suggestions may be implemented, we greatly appreciate your input and strive to enhance the platform based on community insights.
For additional discussions, reach out via bitvid’s Nostr support channels.
`.trim();
log("[DEBUG] Constructed feedback content:\n" + feedbackContent);
// Decode the recipient NPUB to get the public key.
log("Decoding recipient npub...");
const decoded = nip19.decode(recipientNpub);
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
if (decoded.type !== "npub") {
throw new Error("Decoded type is not npub.");
}
const targetPubHex = decoded.data;
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
// Generate an ephemeral key pair.
log("Generating ephemeral key...");
const ephemeralPriv = generateSecretKey();
const ephemeralPubHex = getPublicKey(ephemeralPriv);
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
// Encrypt the feedback content.
log("Encrypting feedback content (nip04)...");
const ciphertext = await nip04.encrypt(
ephemeralPriv,
targetPubHex,
feedbackContent
);
log("[DEBUG] Ciphertext: " + ciphertext);
log("Encryption done.");
// Build the event template.
const now = Math.floor(Date.now() / 1000);
const eventTemplate = {
kind: 4,
created_at: now,
tags: [["p", targetPubHex]],
content: ciphertext,
};
log(
"[DEBUG] Event template before finalizing: " +
JSON.stringify(eventTemplate)
);
// Finalize the event.
const event = finalizeEvent(eventTemplate, ephemeralPriv);
log("[DEBUG] Final event: " + JSON.stringify(event));
// Publish the event to all relays.
log("Publishing the feedback to relays...");
await Promise.any(pool.publish(RELAYS, event));
log("At least one relay accepted the event.", "success");
// Subscribe to each relay to verify storage.
for (const url of RELAYS) {
log("Connecting to " + url + " for subscription...");
const relay = await Relay.connect(url);
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
onEvent(foundEvent) {
if (foundEvent.id === event.id) {
log(
"[" +
url +
"] => Found our feedback in storage! ID: " +
foundEvent.id.slice(0, 8) +
"...",
"success"
);
}
},
onEose() {
relay.close();
},
});
}
log(
"Done. If the logs show that at least one relay accepted the event and the feedback was stored, it will be reviewed accordingly."
);
} catch (err) {
log("Error: " + err.message, "error");
user.error(err);
}
});
});
</script>
<div id="uiOverlay" data-overlay-root aria-hidden="true"></div>
</body>
</html>