mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2025-09-08 15:08:44 +00:00
updated directory structure to fix root Service Worker issues and performance improvements
This commit is contained in:
557
components/iframe_forms/iframe-application-form.html
Normal file
557
components/iframe_forms/iframe-application-form.html
Normal file
@@ -0,0 +1,557 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bitvid Whitelist Application Form</title>
|
||||
<!-- Link to your main stylesheet -->
|
||||
<link rel="stylesheet" href="../../css/style.css" />
|
||||
<style>
|
||||
/* Override for form page to match modal field styling */
|
||||
|
||||
/* Remove width constraints from body so our container can be full width */
|
||||
body {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Override the .container to use the full available width */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card-like container for the form */
|
||||
.form-container {
|
||||
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Labels in a light gray */
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-weight: bold;
|
||||
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||
}
|
||||
|
||||
/* Input, textarea, select mimic modal field styles */
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
margin-bottom: 0.75em;
|
||||
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||
padding: 0.5em;
|
||||
border-radius: 0.375rem; /* rounded-md */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #3b82f6; /* blue-500 */
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #3b82f6;
|
||||
}
|
||||
|
||||
/* Style for checkboxes – display inline with a label */
|
||||
.checkbox-group label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Button styled similarly to modal publish button */
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
background: #3b82f6; /* blue-500 */
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Status log area */
|
||||
#status {
|
||||
margin-top: 1em;
|
||||
padding: 0.5em;
|
||||
background: #111827;
|
||||
white-space: pre-wrap;
|
||||
min-height: 80px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.status-line {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
.error {
|
||||
color: #f87171; /* a red tint */
|
||||
}
|
||||
.success {
|
||||
color: #3b82f6; /* blue-500 */
|
||||
}
|
||||
.warn {
|
||||
color: #facc15; /* a yellow tone */
|
||||
}
|
||||
|
||||
/* Custom Scrollbar styling for WebKit browsers */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
/* Custom Scrollbar styling for Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #3b82f6 transparent;
|
||||
}
|
||||
</style>
|
||||
<!-- Load nostr‑tools v2.10.4 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<p>
|
||||
bitvid is currently in early access. If you would like to request
|
||||
access, please fill out this application. Applications will be
|
||||
reviewed manually, and approvals will be based on alignment with
|
||||
bitvid’s community values.
|
||||
</p>
|
||||
<form id="wl-form">
|
||||
<!-- 1. Applicant Information -->
|
||||
<h2>1. Applicant Information</h2>
|
||||
<label for="applicantNpub">Nostr Public Key (npub):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="applicantNpub"
|
||||
placeholder="Enter your npub"
|
||||
required
|
||||
/>
|
||||
|
||||
<label for="contactMethod"
|
||||
>Preferred Contact Method (if applicable):</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="contactMethod"
|
||||
placeholder="Nostr DM, email, or other"
|
||||
/>
|
||||
|
||||
<label for="username">Username or Alias (if applicable):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
placeholder="Enter your preferred name"
|
||||
/>
|
||||
|
||||
<!-- 2. Content Intent -->
|
||||
<h2>2. Content Intent</h2>
|
||||
<p>
|
||||
What type of content do you plan to upload? (Check all that apply)
|
||||
</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="contentType" value="Educational" />
|
||||
Educational</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="contentType"
|
||||
value="Entertainment"
|
||||
/>
|
||||
Entertainment</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="contentType"
|
||||
value="News & Journalism"
|
||||
/>
|
||||
News & Journalism</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="contentType"
|
||||
value="Creative Works"
|
||||
/>
|
||||
Creative Works</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="contentType"
|
||||
value="Discussions & Opinions"
|
||||
/>
|
||||
Discussions & Opinions</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="contentType" value="Other" />
|
||||
Other</label
|
||||
>
|
||||
</div>
|
||||
<label for="otherContent">Other (please specify):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="otherContent"
|
||||
placeholder="Describe if Other was selected"
|
||||
/>
|
||||
|
||||
<label for="whyJoin">Why do you want to join bitvid?</label>
|
||||
<textarea
|
||||
id="whyJoin"
|
||||
rows="3"
|
||||
placeholder="Explain your motivation"
|
||||
></textarea>
|
||||
|
||||
<label for="priorExperience"
|
||||
>Have you created content on other platforms before?</label
|
||||
>
|
||||
<select id="priorExperience">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<label for="experienceLinks"
|
||||
>If yes, provide links or references to your previous work:</label
|
||||
>
|
||||
<textarea
|
||||
id="experienceLinks"
|
||||
rows="2"
|
||||
placeholder="Paste links or describe your work"
|
||||
></textarea>
|
||||
|
||||
<!-- 3. Community Engagement -->
|
||||
<h2>3. Community Engagement</h2>
|
||||
<label for="familiarGuidelines"
|
||||
>Are you familiar with bitvid’s Community Guidelines?</label
|
||||
>
|
||||
<select id="familiarGuidelines">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<label for="agreeGuidelines"
|
||||
>Do you agree to follow the guidelines and respect decentralized
|
||||
moderation?</label
|
||||
>
|
||||
<select id="agreeGuidelines">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<label for="communityContribution"
|
||||
>How do you plan to contribute positively to the bitvid
|
||||
community?</label
|
||||
>
|
||||
<textarea
|
||||
id="communityContribution"
|
||||
rows="3"
|
||||
placeholder="Explain your approach"
|
||||
></textarea>
|
||||
|
||||
<!-- 4. Additional Information -->
|
||||
<h2>4. Additional Information</h2>
|
||||
<label for="specialSkills"
|
||||
>Do you have any special skills or interests that could help improve
|
||||
bitvid?</label
|
||||
>
|
||||
<textarea
|
||||
id="specialSkills"
|
||||
rows="3"
|
||||
placeholder="e.g., software development, moderation, design, advocacy"
|
||||
></textarea>
|
||||
|
||||
<label for="testFeatures"
|
||||
>Would you be interested in helping test new features?</label
|
||||
>
|
||||
<select id="testFeatures">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<!-- 5. Declaration -->
|
||||
<h2>5. Declaration</h2>
|
||||
<p>
|
||||
By submitting this application, you confirm that:
|
||||
<br />- The information provided is accurate. <br />- You understand
|
||||
that approval is based on community alignment and available
|
||||
capacity. <br />- You acknowledge that whitelist status may be
|
||||
revoked if guidelines are violated.
|
||||
</p>
|
||||
<label for="signature">Signature (Digital or Written):</label>
|
||||
<input type="text" id="signature" placeholder="Your signature" />
|
||||
|
||||
<label for="declarationDate">Date:</label>
|
||||
<input type="date" id="declarationDate" />
|
||||
|
||||
<button type="submit">Submit Application</button>
|
||||
</form>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Logging functions for both 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);
|
||||
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||
}
|
||||
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) here.
|
||||
const recipientNpub =
|
||||
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||
const RELAYS = [
|
||||
"wss://relay.snort.social",
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.primal.net",
|
||||
];
|
||||
const pool = new SimplePool();
|
||||
|
||||
document
|
||||
.getElementById("wl-form")
|
||||
.addEventListener("submit", async (ev) => {
|
||||
ev.preventDefault();
|
||||
clear();
|
||||
try {
|
||||
// Retrieve applicant-provided input.
|
||||
const applicantNpub = document
|
||||
.getElementById("applicantNpub")
|
||||
.value.trim();
|
||||
const contactMethod = document
|
||||
.getElementById("contactMethod")
|
||||
.value.trim();
|
||||
const username = document.getElementById("username").value.trim();
|
||||
|
||||
// Section 2: Content Intent
|
||||
// Get all checked content types
|
||||
const contentTypeNodes = document.querySelectorAll(
|
||||
'input[name="contentType"]:checked'
|
||||
);
|
||||
let contentTypes = [];
|
||||
contentTypeNodes.forEach((node) => {
|
||||
contentTypes.push(node.value);
|
||||
});
|
||||
const otherContent = document
|
||||
.getElementById("otherContent")
|
||||
.value.trim();
|
||||
const whyJoin = document.getElementById("whyJoin").value.trim();
|
||||
const priorExperience = document
|
||||
.getElementById("priorExperience")
|
||||
.value.trim();
|
||||
const experienceLinks = document
|
||||
.getElementById("experienceLinks")
|
||||
.value.trim();
|
||||
|
||||
// Section 3: Community Engagement
|
||||
const familiarGuidelines = document
|
||||
.getElementById("familiarGuidelines")
|
||||
.value.trim();
|
||||
const agreeGuidelines = document
|
||||
.getElementById("agreeGuidelines")
|
||||
.value.trim();
|
||||
const communityContribution = document
|
||||
.getElementById("communityContribution")
|
||||
.value.trim();
|
||||
|
||||
// Section 4: Additional Information
|
||||
const specialSkills = document
|
||||
.getElementById("specialSkills")
|
||||
.value.trim();
|
||||
const testFeatures = document
|
||||
.getElementById("testFeatures")
|
||||
.value.trim();
|
||||
|
||||
// Section 5: Declaration
|
||||
const signature = document
|
||||
.getElementById("signature")
|
||||
.value.trim();
|
||||
const declarationDate = document
|
||||
.getElementById("declarationDate")
|
||||
.value.trim();
|
||||
|
||||
// Construct the whitelist application content.
|
||||
const applicationContent = `
|
||||
# **bitvid Whitelist Application Form**
|
||||
|
||||
**1. Applicant Information**
|
||||
- **Nostr Public Key (npub):** ${applicantNpub}
|
||||
- **Preferred Contact Method:** ${contactMethod || "N/A"}
|
||||
- **Username or Alias:** ${username || "N/A"}
|
||||
|
||||
**2. Content Intent**
|
||||
- **Planned Content Types:** ${contentTypes.join(", ") || "N/A"}
|
||||
- **Other (if applicable):** ${otherContent || "N/A"}
|
||||
- **Why do you want to join bitvid?**
|
||||
${whyJoin || "N/A"}
|
||||
- **Have you created content on other platforms before?** ${
|
||||
priorExperience || "N/A"
|
||||
}
|
||||
- **Links or references to your previous work:**
|
||||
${experienceLinks || "N/A"}
|
||||
|
||||
**3. Community Engagement**
|
||||
- **Familiar with bitvid’s Community Guidelines?** ${
|
||||
familiarGuidelines || "N/A"
|
||||
}
|
||||
- **Agree to follow guidelines and respect moderation?** ${
|
||||
agreeGuidelines || "N/A"
|
||||
}
|
||||
- **How do you plan to contribute to the community?**
|
||||
${communityContribution || "N/A"}
|
||||
|
||||
**4. Additional Information**
|
||||
- **Special skills or interests:**
|
||||
${specialSkills || "N/A"}
|
||||
- **Interested in testing new features?** ${testFeatures || "N/A"}
|
||||
|
||||
**5. Declaration**
|
||||
By submitting this application, you confirm that:
|
||||
- The information provided is accurate.
|
||||
- You understand that approval is based on community alignment and available capacity.
|
||||
- You acknowledge that whitelist status may be revoked if guidelines are violated.
|
||||
|
||||
**Signature (Digital or Written):** ${signature || "N/A"}
|
||||
**Date:** ${declarationDate || "N/A"}
|
||||
|
||||
---
|
||||
|
||||
**Processing Time:** Applications will be reviewed within **7-14 days**. If approved, you will receive a confirmation via your provided contact method. If additional information is needed, we will reach out.
|
||||
|
||||
For further questions, contact us through bitvid’s Nostr support channels.
|
||||
`.trim();
|
||||
|
||||
log(
|
||||
"[DEBUG] Constructed application content:\n" +
|
||||
applicationContent
|
||||
);
|
||||
|
||||
// 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 application content.
|
||||
log("Encrypting application content (nip04)...");
|
||||
const ciphertext = await nip04.encrypt(
|
||||
ephemeralPriv,
|
||||
targetPubHex,
|
||||
applicationContent
|
||||
);
|
||||
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||
log("Encryption done.");
|
||||
|
||||
// Build the Nostr 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 (computing the id and signature).
|
||||
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||
|
||||
// Publish the event to all relays.
|
||||
log("Publishing the application to relays...");
|
||||
await Promise.any(pool.publish(RELAYS, event));
|
||||
log("At least one relay accepted the event.", "success");
|
||||
|
||||
// For each relay, subscribe to verify the event appears.
|
||||
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 application 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 application was found in storage, a moderator will review your application within 7-14 days."
|
||||
);
|
||||
} catch (err) {
|
||||
log("Error: " + err.message, "error");
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
528
components/iframe_forms/iframe-bug-fix-form.html
Normal file
528
components/iframe_forms/iframe-bug-fix-form.html
Normal file
@@ -0,0 +1,528 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bitvid Bug Report Form</title>
|
||||
<!-- Link to your main stylesheet -->
|
||||
<link rel="stylesheet" href="../../css/style.css" />
|
||||
<style>
|
||||
/* Override for form page to match modal field styling */
|
||||
|
||||
/* Remove width constraints from body so our container can be full width */
|
||||
body {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Override the .container to use the full available width */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card-like container for the form, similar to modal-content */
|
||||
.form-container {
|
||||
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Labels in a light gray */
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-weight: bold;
|
||||
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||
}
|
||||
|
||||
/* Input, textarea, and select mimic modal field styles */
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
margin-bottom: 0.75em;
|
||||
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||
padding: 0.5em;
|
||||
border-radius: 0.375rem; /* rounded-md */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #3b82f6; /* blue-500 */
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #3b82f6;
|
||||
}
|
||||
|
||||
/* Checkbox group styling */
|
||||
.checkbox-group label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Button styled similarly to modal publish button */
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
background: #3b82f6; /* blue-500 */
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Status log area */
|
||||
#status {
|
||||
margin-top: 1em;
|
||||
padding: 0.5em;
|
||||
background: #111827;
|
||||
white-space: pre-wrap;
|
||||
min-height: 80px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.status-line {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
.error {
|
||||
color: #f87171; /* a red tint */
|
||||
}
|
||||
.success {
|
||||
color: #3b82f6; /* blue-500 */
|
||||
}
|
||||
.warn {
|
||||
color: #facc15; /* a yellow tone */
|
||||
}
|
||||
|
||||
/* Custom Scrollbar styling for WebKit browsers */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
/* Custom Scrollbar styling for Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #3b82f6 transparent;
|
||||
}
|
||||
</style>
|
||||
<!-- Load nostr‑tools v2.10.4 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<p>
|
||||
If you have encountered a bug or technical issue on bitvid, please
|
||||
fill out this form to help us diagnose and resolve the problem.
|
||||
Providing detailed information will assist us in troubleshooting more
|
||||
efficiently.
|
||||
</p>
|
||||
<form id="bug-form">
|
||||
<!-- Section 1: User Information -->
|
||||
<h2>1. User Information</h2>
|
||||
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="userNpub"
|
||||
placeholder="Enter your npub (optional)"
|
||||
/>
|
||||
<p>Are you a (check all that apply):</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Viewer" />
|
||||
Viewer</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Content Creator" />
|
||||
Content Creator</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="userRole"
|
||||
value="Developer/Contributor"
|
||||
/>
|
||||
Developer / Contributor</label
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Bug Details -->
|
||||
<h2>2. Bug Details</h2>
|
||||
<label for="issueDescription">Describe the issue:</label>
|
||||
<textarea
|
||||
id="issueDescription"
|
||||
rows="3"
|
||||
placeholder="Provide a clear and concise description of the problem"
|
||||
></textarea>
|
||||
<label for="stepsToReproduce">Steps to reproduce the bug:</label>
|
||||
<textarea
|
||||
id="stepsToReproduce"
|
||||
rows="3"
|
||||
placeholder="List the actions taken before encountering the issue"
|
||||
></textarea>
|
||||
<label for="expectedBehavior">Expected behavior:</label>
|
||||
<textarea
|
||||
id="expectedBehavior"
|
||||
rows="2"
|
||||
placeholder="What should have happened instead?"
|
||||
></textarea>
|
||||
<label for="actualBehavior">Actual behavior:</label>
|
||||
<textarea
|
||||
id="actualBehavior"
|
||||
rows="2"
|
||||
placeholder="What actually happened?"
|
||||
></textarea>
|
||||
|
||||
<!-- Section 3: Device & Environment -->
|
||||
<h2>3. Device & Environment</h2>
|
||||
<p>What device were you using? (check all that apply):</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="deviceUsed" value="Desktop" />
|
||||
Desktop</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="deviceUsed" value="Mobile" />
|
||||
Mobile</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="deviceUsed" value="Tablet" />
|
||||
Tablet</label
|
||||
>
|
||||
</div>
|
||||
<label for="operatingSystem">Operating System:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="operatingSystem"
|
||||
placeholder="e.g., Windows, macOS, Linux, iOS, Android"
|
||||
/>
|
||||
<label for="browserInfo">Browser (if applicable):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="browserInfo"
|
||||
placeholder="e.g., Chrome, Firefox, Safari"
|
||||
/>
|
||||
<label for="usingVPN"
|
||||
>Are you using a VPN or privacy-focused browser settings?</label
|
||||
>
|
||||
<select id="usingVPN">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<!-- Section 4: Screenshots or Logs -->
|
||||
<h2>4. Screenshots or Logs (If Available)</h2>
|
||||
<label for="screenshotInfo"
|
||||
>Can you provide a screenshot or screen recording?</label
|
||||
>
|
||||
<textarea
|
||||
id="screenshotInfo"
|
||||
rows="2"
|
||||
placeholder="Attach if possible (e.g., URL to screenshot)"
|
||||
></textarea>
|
||||
<label for="errorMessages"
|
||||
>Did the issue generate any error messages?</label
|
||||
>
|
||||
<textarea
|
||||
id="errorMessages"
|
||||
rows="2"
|
||||
placeholder="Include the exact error messages, if any"
|
||||
></textarea>
|
||||
<label for="consoleLogs">Any relevant console logs?</label>
|
||||
<textarea
|
||||
id="consoleLogs"
|
||||
rows="2"
|
||||
placeholder="Paste any browser console logs if available"
|
||||
></textarea>
|
||||
|
||||
<!-- Section 5: Additional Information -->
|
||||
<h2>5. Additional Information</h2>
|
||||
<label for="bugFrequency">How frequently does this bug occur?</label>
|
||||
<select id="bugFrequency">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Every time">Every time</option>
|
||||
<option value="Occasionally">Occasionally</option>
|
||||
<option value="Rarely">Rarely</option>
|
||||
</select>
|
||||
<label for="impactCore"
|
||||
>Does this bug impact core functionality (e.g., video playback,
|
||||
uploads, etc.)?</label
|
||||
>
|
||||
<select id="impactCore">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
<label for="additionalNotes"
|
||||
>Any additional notes or suggestions?</label
|
||||
>
|
||||
<textarea
|
||||
id="additionalNotes"
|
||||
rows="3"
|
||||
placeholder="Describe any other relevant details"
|
||||
></textarea>
|
||||
|
||||
<button type="submit">Submit Bug Report</button>
|
||||
</form>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Logging functions for both 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);
|
||||
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||
}
|
||||
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("bug-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: Bug Details
|
||||
const issueDescription = document
|
||||
.getElementById("issueDescription")
|
||||
.value.trim();
|
||||
const stepsToReproduce = document
|
||||
.getElementById("stepsToReproduce")
|
||||
.value.trim();
|
||||
const expectedBehavior = document
|
||||
.getElementById("expectedBehavior")
|
||||
.value.trim();
|
||||
const actualBehavior = document
|
||||
.getElementById("actualBehavior")
|
||||
.value.trim();
|
||||
|
||||
// Section 3: Device & Environment
|
||||
const deviceNodes = document.querySelectorAll(
|
||||
'input[name="deviceUsed"]:checked'
|
||||
);
|
||||
let devicesUsed = [];
|
||||
deviceNodes.forEach((node) => {
|
||||
devicesUsed.push(node.value);
|
||||
});
|
||||
const operatingSystem = document
|
||||
.getElementById("operatingSystem")
|
||||
.value.trim();
|
||||
const browserInfo = document
|
||||
.getElementById("browserInfo")
|
||||
.value.trim();
|
||||
const usingVPN = document.getElementById("usingVPN").value.trim();
|
||||
|
||||
// Section 4: Screenshots or Logs
|
||||
const screenshotInfo = document
|
||||
.getElementById("screenshotInfo")
|
||||
.value.trim();
|
||||
const errorMessages = document
|
||||
.getElementById("errorMessages")
|
||||
.value.trim();
|
||||
const consoleLogs = document
|
||||
.getElementById("consoleLogs")
|
||||
.value.trim();
|
||||
|
||||
// Section 5: Additional Information
|
||||
const bugFrequency = document
|
||||
.getElementById("bugFrequency")
|
||||
.value.trim();
|
||||
const impactCore = document
|
||||
.getElementById("impactCore")
|
||||
.value.trim();
|
||||
const additionalNotes = document
|
||||
.getElementById("additionalNotes")
|
||||
.value.trim();
|
||||
|
||||
// Construct the bug report content as Markdown.
|
||||
const bugReportContent = `
|
||||
# **bitvid Bug Report Form**
|
||||
|
||||
If you have encountered a bug or technical issue on bitvid, please fill out this form to help us diagnose and resolve the problem. Providing detailed information will assist us in troubleshooting more efficiently.
|
||||
|
||||
## **1. User Information**
|
||||
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||
- **Roles:** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
||||
|
||||
## **2. Bug Details**
|
||||
- **Describe the issue:**
|
||||
${issueDescription || "N/A"}
|
||||
- **Steps to reproduce the bug:**
|
||||
${stepsToReproduce || "N/A"}
|
||||
- **Expected behavior:**
|
||||
${expectedBehavior || "N/A"}
|
||||
- **Actual behavior:**
|
||||
${actualBehavior || "N/A"}
|
||||
|
||||
## **3. Device & Environment**
|
||||
- **Devices used:** ${devicesUsed.length > 0 ? devicesUsed.join(", ") : "N/A"}
|
||||
- **Operating System:** ${operatingSystem || "N/A"}
|
||||
- **Browser:** ${browserInfo || "N/A"}
|
||||
- **Using VPN/Privacy Settings:** ${usingVPN || "N/A"}
|
||||
|
||||
## **4. Screenshots or Logs**
|
||||
- **Screenshot/Recording:** ${screenshotInfo || "N/A"}
|
||||
- **Error Messages:** ${errorMessages || "N/A"}
|
||||
- **Console Logs:** ${consoleLogs || "N/A"}
|
||||
|
||||
## **5. Additional Information**
|
||||
- **Bug Frequency:** ${bugFrequency || "N/A"}
|
||||
- **Impact on Core Functionality:** ${impactCore || "N/A"}
|
||||
- **Additional Notes:** ${additionalNotes || "N/A"}
|
||||
|
||||
---
|
||||
|
||||
**Processing & Resolution:**
|
||||
Our team reviews bug reports regularly. While we aim to fix critical issues as soon as possible, some bugs may take longer to resolve. We appreciate your patience and help in improving bitvid!
|
||||
|
||||
For urgent issues, contact us through bitvid’s Nostr support channels.
|
||||
`.trim();
|
||||
|
||||
log(
|
||||
"[DEBUG] Constructed bug report content:\n" + bugReportContent
|
||||
);
|
||||
|
||||
// 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 bug report content.
|
||||
log("Encrypting bug report content (nip04)...");
|
||||
const ciphertext = await nip04.encrypt(
|
||||
ephemeralPriv,
|
||||
targetPubHex,
|
||||
bugReportContent
|
||||
);
|
||||
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 bug report to relays...");
|
||||
await Promise.any(pool.publish(RELAYS, event));
|
||||
log("At least one relay accepted the event.", "success");
|
||||
|
||||
// Subscribe to each relay.
|
||||
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 bug report 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 bug report was found in storage, our team will review your report within 7-14 days."
|
||||
);
|
||||
} catch (err) {
|
||||
log("Error: " + err.message, "error");
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
461
components/iframe_forms/iframe-content-appeals-form.html
Normal file
461
components/iframe_forms/iframe-content-appeals-form.html
Normal file
@@ -0,0 +1,461 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bitvid Content Appeals Form</title>
|
||||
<!-- Link to your main stylesheet -->
|
||||
<link rel="stylesheet" href="../../css/style.css" />
|
||||
<style>
|
||||
/* Override for form page to match modal field styling */
|
||||
|
||||
/* Remove width constraints from body so our container can be full width */
|
||||
body {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Override the .container to use the full available width */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card-like container for the form, similar to modal-content */
|
||||
.form-container {
|
||||
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Labels in a light gray */
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-weight: bold;
|
||||
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||
}
|
||||
|
||||
/* Input, textarea, and select mimic modal field styles */
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
margin-bottom: 0.75em;
|
||||
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||
padding: 0.5em;
|
||||
border-radius: 0.375rem; /* rounded-md */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #3b82f6; /* blue-500 */
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #3b82f6;
|
||||
}
|
||||
|
||||
/* Button styled similarly to modal publish button */
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
background: #3b82f6; /* blue-500 */
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Status log area */
|
||||
#status {
|
||||
margin-top: 1em;
|
||||
padding: 0.5em;
|
||||
background: #111827;
|
||||
white-space: pre-wrap;
|
||||
min-height: 80px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.status-line {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
.error {
|
||||
color: #f87171; /* a red tint */
|
||||
}
|
||||
.success {
|
||||
color: #3b82f6; /* blue-500 */
|
||||
}
|
||||
.warn {
|
||||
color: #facc15; /* a yellow tone */
|
||||
}
|
||||
|
||||
/* Custom Scrollbar styling for WebKit browsers */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
/* Custom Scrollbar styling for Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #3b82f6 transparent;
|
||||
}
|
||||
</style>
|
||||
<!-- Load nostr‑tools v2.10.4 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<p>
|
||||
If you believe your content was unfairly blocked or restricted on
|
||||
bitvid, please complete this form. Appeals will be reviewed manually,
|
||||
and decisions will be communicated back to you.
|
||||
</p>
|
||||
<form id="dm-form">
|
||||
<!-- NPUB input removed as the recipient is now set by the code -->
|
||||
<h2>1. User Information</h2>
|
||||
<label for="contactMethod">Contact Method (if applicable):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="contactMethod"
|
||||
placeholder="Nostr DM, email, or other"
|
||||
/>
|
||||
|
||||
<h2>2. Content Details</h2>
|
||||
<label for="videoTitle">Title of the Video:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="videoTitle"
|
||||
placeholder="Enter the exact title"
|
||||
/>
|
||||
|
||||
<label for="magnetLink">Magnet Link:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="magnetLink"
|
||||
placeholder="Enter the magnet link"
|
||||
/>
|
||||
|
||||
<label for="submissionDate">Date of Content Submission:</label>
|
||||
<input type="date" id="submissionDate" />
|
||||
|
||||
<h2>3. Reason for Appeal</h2>
|
||||
<label for="reasonBlocked"
|
||||
>Why do you believe your content was unfairly blocked?</label
|
||||
>
|
||||
<textarea
|
||||
id="reasonBlocked"
|
||||
rows="3"
|
||||
placeholder="Explain in detail"
|
||||
></textarea>
|
||||
|
||||
<label for="fitsGuidelines"
|
||||
>Does your content fit within bitvid's Community Guidelines?</label
|
||||
>
|
||||
<select id="fitsGuidelines">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<label for="guidelinesCited"
|
||||
>If yes, which guideline(s) support your appeal?</label
|
||||
>
|
||||
<textarea
|
||||
id="guidelinesCited"
|
||||
rows="2"
|
||||
placeholder="Cite the specific guidelines"
|
||||
></textarea>
|
||||
|
||||
<label for="editedContent"
|
||||
>Was this content edited after being blocked?</label
|
||||
>
|
||||
<select id="editedContent">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<label for="changesMade">If yes, what changes were made?</label>
|
||||
<textarea
|
||||
id="changesMade"
|
||||
rows="2"
|
||||
placeholder="Describe the modifications"
|
||||
></textarea>
|
||||
|
||||
<h2>4. Additional Context</h2>
|
||||
<label for="misunderstanding"
|
||||
>Was there any misunderstanding or misclassification?</label
|
||||
>
|
||||
<textarea
|
||||
id="misunderstanding"
|
||||
rows="2"
|
||||
placeholder="Provide context"
|
||||
></textarea>
|
||||
|
||||
<label for="externalReferences"
|
||||
>Are there external references that validate your appeal?</label
|
||||
>
|
||||
<textarea
|
||||
id="externalReferences"
|
||||
rows="2"
|
||||
placeholder="Links, citations, or additional info"
|
||||
></textarea>
|
||||
|
||||
<h2>5. Declaration</h2>
|
||||
<p>
|
||||
By submitting this appeal, you confirm that:
|
||||
<br />- You are the original creator or authorized representative of
|
||||
the content. <br />- Your appeal is submitted in good faith and
|
||||
aligns with bitvid’s policies. <br />- You understand that final
|
||||
decisions are at the discretion of bitvid’s moderation process.
|
||||
</p>
|
||||
<label for="signature">Signature (Digital or Written):</label>
|
||||
<input type="text" id="signature" placeholder="Your signature" />
|
||||
|
||||
<label for="declarationDate">Date:</label>
|
||||
<input type="date" id="declarationDate" />
|
||||
|
||||
<button type="submit">Submit Appeal</button>
|
||||
</form>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Logging functions for both 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);
|
||||
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||
}
|
||||
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) here.
|
||||
const recipientNpub =
|
||||
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||
|
||||
const RELAYS = [
|
||||
"wss://relay.snort.social",
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.primal.net",
|
||||
];
|
||||
const pool = new SimplePool();
|
||||
|
||||
document
|
||||
.getElementById("dm-form")
|
||||
.addEventListener("submit", async (ev) => {
|
||||
ev.preventDefault();
|
||||
clear();
|
||||
try {
|
||||
// Note: The customer's NPUB input is removed.
|
||||
// Use the recipientNpub constant to get the public key.
|
||||
|
||||
const contactMethod = document
|
||||
.getElementById("contactMethod")
|
||||
.value.trim();
|
||||
const videoTitle = document
|
||||
.getElementById("videoTitle")
|
||||
.value.trim();
|
||||
const magnetLink = document
|
||||
.getElementById("magnetLink")
|
||||
.value.trim();
|
||||
const submissionDate = document
|
||||
.getElementById("submissionDate")
|
||||
.value.trim();
|
||||
const reasonBlocked = document
|
||||
.getElementById("reasonBlocked")
|
||||
.value.trim();
|
||||
const fitsGuidelines = document
|
||||
.getElementById("fitsGuidelines")
|
||||
.value.trim();
|
||||
const guidelinesCited = document
|
||||
.getElementById("guidelinesCited")
|
||||
.value.trim();
|
||||
const editedContent = document
|
||||
.getElementById("editedContent")
|
||||
.value.trim();
|
||||
const changesMade = document
|
||||
.getElementById("changesMade")
|
||||
.value.trim();
|
||||
const misunderstanding = document
|
||||
.getElementById("misunderstanding")
|
||||
.value.trim();
|
||||
const externalReferences = document
|
||||
.getElementById("externalReferences")
|
||||
.value.trim();
|
||||
const signature = document
|
||||
.getElementById("signature")
|
||||
.value.trim();
|
||||
const declarationDate = document
|
||||
.getElementById("declarationDate")
|
||||
.value.trim();
|
||||
|
||||
// Construct the appeal content.
|
||||
const appealContent = `
|
||||
# **bitvid Content Appeals Form**
|
||||
|
||||
**1. User Information**
|
||||
- **Contact Method:** ${contactMethod || "N/A"}
|
||||
|
||||
**2. Content Details**
|
||||
- **Title of the Video:** ${videoTitle}
|
||||
- **Magnet Link:** ${magnetLink}
|
||||
- **Date of Content Submission:** ${submissionDate}
|
||||
|
||||
**3. Reason for Appeal**
|
||||
- **Why do you believe your content was unfairly blocked?**
|
||||
${reasonBlocked}
|
||||
- **Does your content fit within bitvid's Community Guidelines?**
|
||||
${fitsGuidelines}
|
||||
- **If yes, which guideline(s) support your appeal?**
|
||||
${guidelinesCited}
|
||||
- **Was this content edited after being blocked?**
|
||||
${editedContent}
|
||||
- **If yes, what changes were made?**
|
||||
${changesMade}
|
||||
|
||||
**4. Additional Context**
|
||||
- **Was there any misunderstanding or misclassification?**
|
||||
${misunderstanding}
|
||||
- **Are there external references that validate your appeal?**
|
||||
${externalReferences}
|
||||
|
||||
**5. Declaration**
|
||||
By submitting this appeal, you confirm that:
|
||||
- You are the original creator or an authorized representative of the content.
|
||||
- Your appeal is submitted in good faith and aligns with bitvid’s policies.
|
||||
- You understand that final decisions are at the discretion of bitvid’s moderation process.
|
||||
|
||||
**Signature (Digital or Written):** ${signature}
|
||||
**Date:** ${declarationDate}
|
||||
|
||||
---
|
||||
|
||||
**Processing Time:** Appeals will be reviewed within **7-14 days**. If additional information is required, you will be contacted via your provided contact method.
|
||||
|
||||
For further questions, reach out through bitvid’s Nostr support channels.
|
||||
`.trim();
|
||||
|
||||
log("[DEBUG] Constructed appeal content:\n" + appealContent);
|
||||
|
||||
// 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 appeal content.
|
||||
log("Encrypting appeal content (nip04)...");
|
||||
const ciphertext = await nip04.encrypt(
|
||||
ephemeralPriv,
|
||||
targetPubHex,
|
||||
appealContent
|
||||
);
|
||||
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 appeal to relays...");
|
||||
await Promise.any(pool.publish(RELAYS, event));
|
||||
log("At least one relay accepted the event.", "success");
|
||||
|
||||
// Subscribe to each relay.
|
||||
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 appeal 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 appeal was found in storage, a moderator will review your appeal within 7-14 days."
|
||||
);
|
||||
} catch (err) {
|
||||
log("Error: " + err.message, "error");
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
454
components/iframe_forms/iframe-feedback-form.html
Normal file
454
components/iframe_forms/iframe-feedback-form.html
Normal file
@@ -0,0 +1,454 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bitvid General Feedback Form</title>
|
||||
<!-- Link to your main stylesheet -->
|
||||
<link rel="stylesheet" href="../../css/style.css" />
|
||||
<style>
|
||||
/* Override for form page to match modal field styling */
|
||||
|
||||
/* Remove width constraints from body so our container can be full width */
|
||||
body {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Override the .container to use the full available width */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card-like container for the form */
|
||||
.form-container {
|
||||
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Labels in a light gray */
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-weight: bold;
|
||||
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||
}
|
||||
|
||||
/* Input, textarea, and select styling */
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
margin-bottom: 0.75em;
|
||||
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||
padding: 0.5em;
|
||||
border-radius: 0.375rem; /* rounded-md */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #3b82f6; /* blue-500 */
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #3b82f6;
|
||||
}
|
||||
|
||||
/* Checkbox group styling */
|
||||
.checkbox-group label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Radio group styling (for experience rating) */
|
||||
.radio-group label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
background: #3b82f6; /* blue-500 */
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Status log area */
|
||||
#status {
|
||||
margin-top: 1em;
|
||||
padding: 0.5em;
|
||||
background: #111827;
|
||||
white-space: pre-wrap;
|
||||
min-height: 80px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.status-line {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
.error {
|
||||
color: #f87171;
|
||||
}
|
||||
.success {
|
||||
color: #3b82f6;
|
||||
}
|
||||
.warn {
|
||||
color: #facc15;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar styling for WebKit browsers */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
/* Custom Scrollbar styling for Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #3b82f6 transparent;
|
||||
}
|
||||
</style>
|
||||
<!-- Load nostr‑tools v2.10.4 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<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
|
||||
type="text"
|
||||
id="userNpub"
|
||||
placeholder="Enter your npub (optional)"
|
||||
/>
|
||||
<p>Are you a (check all that apply):</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Viewer" />
|
||||
Viewer</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Content Creator" />
|
||||
Content Creator</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 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
|
||||
type="text"
|
||||
id="preferredContact"
|
||||
placeholder="Nostr DM, email, or other"
|
||||
/>
|
||||
|
||||
<button type="submit">Submit General Feedback</button>
|
||||
</form>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
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);
|
||||
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||
}
|
||||
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");
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
471
components/iframe_forms/iframe-request-form.html
Normal file
471
components/iframe_forms/iframe-request-form.html
Normal file
@@ -0,0 +1,471 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bitvid Feature Request Form</title>
|
||||
<!-- Link to your main stylesheet -->
|
||||
<link rel="stylesheet" href="../../css/style.css" />
|
||||
<style>
|
||||
/* Override for form page to match modal field styling */
|
||||
|
||||
/* Remove width constraints from body so our container can be full width */
|
||||
body {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Full-width container */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card-like container for the form */
|
||||
.form-container {
|
||||
background-color: #111827;
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-weight: bold;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* Input, textarea, and select styling */
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
margin-bottom: 0.75em;
|
||||
background-color: #1f2937;
|
||||
color: #f3f4f6;
|
||||
border: 1px solid #374151;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.375rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #3b82f6;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #3b82f6;
|
||||
}
|
||||
|
||||
/* Checkbox group styling */
|
||||
.checkbox-group label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
background: #3b82f6;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Status log area */
|
||||
#status {
|
||||
margin-top: 1em;
|
||||
padding: 0.5em;
|
||||
background: #111827;
|
||||
white-space: pre-wrap;
|
||||
min-height: 80px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.status-line {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
.error {
|
||||
color: #f87171;
|
||||
}
|
||||
.success {
|
||||
color: #3b82f6;
|
||||
}
|
||||
.warn {
|
||||
color: #facc15;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #3b82f6 transparent;
|
||||
}
|
||||
</style>
|
||||
<!-- Load nostr‑tools v2.10.4 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<p>
|
||||
Have an idea for improving bitvid? We’d love to hear it! Please use
|
||||
this form to request new features or enhancements. Your feedback helps
|
||||
shape the future of bitvid.
|
||||
</p>
|
||||
<form id="feature-form">
|
||||
<!-- Section 1: User Information -->
|
||||
<h2>1. User Information</h2>
|
||||
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||
<input
|
||||
type="text"
|
||||
id="userNpub"
|
||||
placeholder="Enter your npub (optional)"
|
||||
/>
|
||||
<p>Are you a (check all that apply):</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Viewer" />
|
||||
Viewer</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="userRole" value="Content Creator" />
|
||||
Content Creator</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
name="userRole"
|
||||
value="Developer/Contributor"
|
||||
/>
|
||||
Developer / Contributor</label
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Feature Request Details -->
|
||||
<h2>2. Feature Request Details</h2>
|
||||
<label for="featureName">Feature Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="featureName"
|
||||
placeholder="Short, descriptive title"
|
||||
required
|
||||
/>
|
||||
<label for="featureDescription">Describe the feature:</label>
|
||||
<textarea
|
||||
id="featureDescription"
|
||||
rows="3"
|
||||
placeholder="Explain in detail what the feature does and how it works"
|
||||
></textarea>
|
||||
<label for="featureImportance">Why is this feature important?</label>
|
||||
<textarea
|
||||
id="featureImportance"
|
||||
rows="2"
|
||||
placeholder="Describe how this will improve the platform and benefit users"
|
||||
></textarea>
|
||||
<label for="beneficiary">Who would benefit from this feature?</label>
|
||||
<input
|
||||
type="text"
|
||||
id="beneficiary"
|
||||
placeholder="e.g., Content Creators, Viewers, Both"
|
||||
/>
|
||||
|
||||
<!-- Section 3: Additional Information -->
|
||||
<h2>3. Additional Information</h2>
|
||||
<label for="existingPlatforms"
|
||||
>Are there existing platforms that have this feature?</label
|
||||
>
|
||||
<textarea
|
||||
id="existingPlatforms"
|
||||
rows="2"
|
||||
placeholder="Provide examples if applicable"
|
||||
></textarea>
|
||||
<label for="mockups">Do you have any mockups or examples?</label>
|
||||
<textarea
|
||||
id="mockups"
|
||||
rows="2"
|
||||
placeholder="Links, screenshots, or diagrams"
|
||||
></textarea>
|
||||
<label for="willingToTest"
|
||||
>Would you be willing to help test this feature if
|
||||
implemented?</label
|
||||
>
|
||||
<select id="willingToTest">
|
||||
<option value="">Select an option</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
|
||||
<!-- Section 4: Priority & Impact -->
|
||||
<h2>4. Priority & Impact</h2>
|
||||
<p>How urgent is this feature?</p>
|
||||
<div class="checkbox-group">
|
||||
<label
|
||||
><input type="checkbox" name="featurePriority" value="High" />
|
||||
High (Essential for platform success)</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="featurePriority" value="Medium" />
|
||||
Medium (Would improve experience significantly)</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="featurePriority" value="Low" /> Low
|
||||
(Nice to have, but not urgent)</label
|
||||
>
|
||||
</div>
|
||||
<label for="techChallenges"
|
||||
>Does this feature require technical expertise to implement?</label
|
||||
>
|
||||
<textarea
|
||||
id="techChallenges"
|
||||
rows="2"
|
||||
placeholder="Describe any dependencies or challenges"
|
||||
></textarea>
|
||||
|
||||
<button type="submit">Submit Feature Request</button>
|
||||
</form>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Logging function
|
||||
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);
|
||||
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||
}
|
||||
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("feature-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: Feature Request Details
|
||||
const featureName = document
|
||||
.getElementById("featureName")
|
||||
.value.trim();
|
||||
const featureDescription = document
|
||||
.getElementById("featureDescription")
|
||||
.value.trim();
|
||||
const featureImportance = document
|
||||
.getElementById("featureImportance")
|
||||
.value.trim();
|
||||
const beneficiary = document
|
||||
.getElementById("beneficiary")
|
||||
.value.trim();
|
||||
|
||||
// Section 3: Additional Information
|
||||
const existingPlatforms = document
|
||||
.getElementById("existingPlatforms")
|
||||
.value.trim();
|
||||
const mockups = document.getElementById("mockups").value.trim();
|
||||
const willingToTest = document
|
||||
.getElementById("willingToTest")
|
||||
.value.trim();
|
||||
|
||||
// Section 4: Priority & Impact
|
||||
const priorityNodes = document.querySelectorAll(
|
||||
'input[name="featurePriority"]:checked'
|
||||
);
|
||||
let featurePriorities = [];
|
||||
priorityNodes.forEach((node) => {
|
||||
featurePriorities.push(node.value);
|
||||
});
|
||||
const techChallenges = document
|
||||
.getElementById("techChallenges")
|
||||
.value.trim();
|
||||
|
||||
// Construct Markdown feature request
|
||||
const featureRequestContent = `
|
||||
# **bitvid Feature Request Form**
|
||||
|
||||
Have an idea for improving bitvid? We’d love to hear it! Please use this form to request new features or enhancements. Your feedback helps shape the future of bitvid.
|
||||
|
||||
## **1. User Information**
|
||||
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||
- **Roles:** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
||||
|
||||
## **2. Feature Request Details**
|
||||
- **Feature Name:** ${featureName || "N/A"}
|
||||
- **Describe the feature:**
|
||||
${featureDescription || "N/A"}
|
||||
- **Why is this feature important?**
|
||||
${featureImportance || "N/A"}
|
||||
- **Who would benefit from this feature?**
|
||||
${beneficiary || "N/A"}
|
||||
|
||||
## **3. Additional Information**
|
||||
- **Are there existing platforms that have this feature?**
|
||||
${existingPlatforms || "N/A"}
|
||||
- **Do you have any mockups or examples?**
|
||||
${mockups || "N/A"}
|
||||
- **Would you be willing to help test this feature if implemented?** ${
|
||||
willingToTest || "N/A"
|
||||
}
|
||||
|
||||
## **4. Priority & Impact**
|
||||
- **How urgent is this feature?**
|
||||
${featurePriorities.length > 0 ? featurePriorities.join(", ") : "N/A"}
|
||||
- **Does this feature require technical expertise to implement?**
|
||||
${techChallenges || "N/A"}
|
||||
|
||||
---
|
||||
|
||||
### **Processing & Consideration**
|
||||
All feature requests are reviewed, but not all may be implemented. We prioritize based on user demand, feasibility, and impact on bitvid. Thank you for helping us improve!
|
||||
|
||||
For further discussions, reach out through bitvid’s Nostr support channels.
|
||||
`.trim();
|
||||
|
||||
log(
|
||||
"[DEBUG] Constructed feature request content:\n" +
|
||||
featureRequestContent
|
||||
);
|
||||
|
||||
// Decode the recipient NPUB
|
||||
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 feature request content
|
||||
log("Encrypting feature request content (nip04)...");
|
||||
const ciphertext = await nip04.encrypt(
|
||||
ephemeralPriv,
|
||||
targetPubHex,
|
||||
featureRequestContent
|
||||
);
|
||||
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 to relays
|
||||
log("Publishing the feature request to relays...");
|
||||
await Promise.any(pool.publish(RELAYS, event));
|
||||
log("At least one relay accepted the event.", "success");
|
||||
|
||||
// Subscribe to verify event 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 feature request 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 feature request was stored, it will be reviewed accordingly."
|
||||
);
|
||||
} catch (err) {
|
||||
log("Error: " + err.message, "error");
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user