updated iframe style

This commit is contained in:
2025-02-02 20:42:18 -05:00
parent d8fb0d378b
commit a5f932b681

View File

@@ -3,204 +3,214 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>bitvid Content Appeals Form</title> <title>bitvid Content Appeals Form</title>
<!-- Link to your main stylesheet -->
<link rel="stylesheet" href="style.css" />
<style> <style>
/* Basic styling for the form and log output */ /* Override styles to match the rest of the app */
/* Make the background transparent and use system fonts */
body { body {
background: #222; background-color: transparent;
color: #eee;
font-family: sans-serif;
margin: 20px; margin: 20px;
max-width: 800px; max-width: 800px;
font-family: system-ui, -apple-system, sans-serif;
} }
/* Container for the form card */
.form-container {
background-color: var(--color-card);
padding: 1.5rem;
border-radius: 0.5rem;
box-shadow: var(--shadow-md);
}
h1, h1,
h2 { h2 {
color: #66ff66; color: var(--color-primary);
} }
label { label {
display: block; display: block;
margin-top: 1em; margin-top: 1em;
font-weight: bold; font-weight: bold;
color: var(--color-text);
} }
input, input,
textarea, textarea,
select { select {
width: 100%; width: 100%;
margin-bottom: 0.75em; margin-bottom: 0.75em;
background: #333; background: var(--color-bg);
color: #fff; color: var(--color-text);
border: 1px solid #888; border: 1px solid var(--color-muted);
padding: 0.5em; padding: 0.5em;
border-radius: 0.25rem;
box-sizing: border-box; box-sizing: border-box;
} }
button { button {
padding: 0.5em 1em; padding: 0.5em 1em;
background: #3399cc; background: var(--color-primary);
color: #fff; color: #fff;
border: none; border: none;
border-radius: 4px; border-radius: 0.375rem;
cursor: pointer; cursor: pointer;
} }
#status { #status {
margin-top: 1em; margin-top: 1em;
padding: 0.5em; padding: 0.5em;
background: #111; background: var(--color-card);
white-space: pre-wrap; white-space: pre-wrap;
min-height: 80px; min-height: 80px;
border-radius: 0.25rem;
} }
.status-line { .status-line {
margin: 0.25em 0; margin: 0.25em 0;
} }
.error { .error {
color: #ff6666; color: var(--color-secondary);
} }
.success { .success {
color: #66ff66; color: var(--color-primary);
} }
.warn { .warn {
color: #ffff66; color: var(--color-muted);
}
/* Custom Scrollbar styling for WebKit */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: var(--color-primary);
border-radius: 4px;
}
/* Custom Scrollbar styling for Firefox */
* {
scrollbar-width: thin;
scrollbar-color: var(--color-primary) transparent;
} }
</style> </style>
<!--
bitvid Content Appeals Form
Key Points and Lessons Learned:
1. We use nostrtools v2.10.4 to send a Nostr event that contains the appeal data.
- The target npub is decoded (using nip19.decode) to extract the target public key.
- An ephemeral key pair is generated with generateSecretKey (returns a Uint8Array) and getPublicKey (returns a hex string).
2. The form collects detailed appeal information (user info, content details, reasons, additional context, and declaration).
3. On submission, all form field values are assembled into a Markdownformatted message.
4. The message is encrypted using nip04.encrypt with the ephemeral private key and the target public key.
5. The event template (kind 4) is built with the encrypted message and then finalized via finalizeEvent,
which automatically computes event.id, assigns the ephemeral pubkey, and signs the event.
6. The event is published to multiple relays using SimplePool.publish (which now returns a promise), and we use Promise.any
to wait for at least one relay to accept the event.
7. For subscriptions, we use the Relay API (Relay.connect and relay.subscribe) to verify that the event appears in storage.
This implementation leverages the higherlevel APIs provided by nostrtools to simplify event creation,
signing, and relay interaction while providing detailed logging for debugging purposes.
-->
<!-- Load nostrtools v2.10.4 --> <!-- Load nostrtools v2.10.4 -->
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
</head> </head>
<body> <body>
<h1>bitvid Content Appeals Form</h1> <div class="container">
<p> <div class="form-container">
If you believe your content was unfairly blocked or restricted on bitvid, <h1>bitvid Content Appeals Form</h1>
please complete this form. Appeals will be reviewed manually, and <p>
decisions will be communicated back to you. If you believe your content was unfairly blocked or restricted on bitvid,
</p> please complete this form. Appeals will be reviewed manually, and
decisions will be communicated back to you.
</p>
<form id="dm-form"> <form id="dm-form">
<h2>1. User Information</h2> <h2>1. User Information</h2>
<label for="npubInput">Nostr Public Key (npub):</label> <label for="npubInput">Nostr Public Key (npub):</label>
<input <input
type="text" type="text"
id="npubInput" id="npubInput"
placeholder="Enter your npub" placeholder="Enter your npub"
required required
/> />
<label for="contactMethod">Contact Method (if applicable):</label> <label for="contactMethod">Contact Method (if applicable):</label>
<input <input
type="text" type="text"
id="contactMethod" id="contactMethod"
placeholder="Nostr DM, email, or other" placeholder="Nostr DM, email, or other"
/> />
<h2>2. Content Details</h2> <h2>2. Content Details</h2>
<label for="videoTitle">Title of the Video:</label> <label for="videoTitle">Title of the Video:</label>
<input type="text" id="videoTitle" placeholder="Enter the exact title" /> <input type="text" id="videoTitle" placeholder="Enter the exact title" />
<label for="magnetLink">Magnet Link:</label> <label for="magnetLink">Magnet Link:</label>
<input type="text" id="magnetLink" placeholder="Enter the magnet link" /> <input type="text" id="magnetLink" placeholder="Enter the magnet link" />
<label for="submissionDate">Date of Content Submission:</label> <label for="submissionDate">Date of Content Submission:</label>
<input type="date" id="submissionDate" /> <input type="date" id="submissionDate" />
<h2>3. Reason for Appeal</h2> <h2>3. Reason for Appeal</h2>
<label for="reasonBlocked" <label for="reasonBlocked">Why do you believe your content was unfairly blocked?</label>
>Why do you believe your content was unfairly blocked?</label <textarea
> id="reasonBlocked"
<textarea rows="3"
id="reasonBlocked" placeholder="Explain in detail"
rows="3" ></textarea>
placeholder="Explain in detail"
></textarea>
<label for="fitsGuidelines" <label for="fitsGuidelines">Does your content fit within bitvid's Community Guidelines?</label>
>Does your content fit within bitvid's Community Guidelines?</label <select id="fitsGuidelines">
> <option value="">Select an option</option>
<select id="fitsGuidelines"> <option value="Yes">Yes</option>
<option value="">Select an option</option> <option value="No">No</option>
<option value="Yes">Yes</option> </select>
<option value="No">No</option>
</select>
<label for="guidelinesCited" <label for="guidelinesCited">If yes, which guideline(s) support your appeal?</label>
>If yes, which guideline(s) support your appeal?</label <textarea
> id="guidelinesCited"
<textarea rows="2"
id="guidelinesCited" placeholder="Cite the specific guidelines"
rows="2" ></textarea>
placeholder="Cite the specific guidelines"
></textarea>
<label for="editedContent" <label for="editedContent">Was this content edited after being blocked?</label>
>Was this content edited after being blocked?</label <select id="editedContent">
> <option value="">Select an option</option>
<select id="editedContent"> <option value="Yes">Yes</option>
<option value="">Select an option</option> <option value="No">No</option>
<option value="Yes">Yes</option> </select>
<option value="No">No</option>
</select>
<label for="changesMade">If yes, what changes were made?</label> <label for="changesMade">If yes, what changes were made?</label>
<textarea <textarea
id="changesMade" id="changesMade"
rows="2" rows="2"
placeholder="Describe the modifications" placeholder="Describe the modifications"
></textarea> ></textarea>
<h2>4. Additional Context</h2> <h2>4. Additional Context</h2>
<label for="misunderstanding" <label for="misunderstanding">Was there any misunderstanding or misclassification?</label>
>Was there any misunderstanding or misclassification?</label <textarea
> id="misunderstanding"
<textarea rows="2"
id="misunderstanding" placeholder="Provide context"
rows="2" ></textarea>
placeholder="Provide context"
></textarea>
<label for="externalReferences" <label for="externalReferences">Are there external references that validate your appeal?</label>
>Are there external references that validate your appeal?</label <textarea
> id="externalReferences"
<textarea rows="2"
id="externalReferences" placeholder="Links, citations, or additional info"
rows="2" ></textarea>
placeholder="Links, citations, or additional info"
></textarea>
<h2>5. Declaration</h2> <h2>5. Declaration</h2>
<p> <p>
By submitting this appeal, you confirm that: By submitting this appeal, you confirm that:
<br />- You are the original creator or authorized representative of the <br />- You are the original creator or authorized representative of the content.
content. <br />- Your appeal is submitted in good faith and aligns with <br />- Your appeal is submitted in good faith and aligns with bitvids policies.
bitvids policies. <br />- You understand that final decisions are at <br />- You understand that final decisions are at the discretion of bitvids moderation process.
the discretion of bitvids moderation process. </p>
</p> <label for="signature">Signature (Digital or Written):</label>
<label for="signature">Signature (Digital or Written):</label> <input type="text" id="signature" placeholder="Your signature" />
<input type="text" id="signature" placeholder="Your signature" />
<label for="declarationDate">Date:</label> <label for="declarationDate">Date:</label>
<input type="date" id="declarationDate" /> <input type="date" id="declarationDate" />
<button type="submit">Submit Appeal</button> <button type="submit">Submit Appeal</button>
</form> </form>
<div id="status"></div> <div id="status"></div>
</div>
</div>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
@@ -224,8 +234,6 @@
return; return;
} }
// Destructure the required functions and classes from nostr-tools.
// finalizeEvent automatically assigns the pubkey, computes event.id, and signs the event.
const { const {
generateSecretKey, generateSecretKey,
getPublicKey, getPublicKey,
@@ -233,75 +241,43 @@
nip04, nip04,
nip19, nip19,
SimplePool, SimplePool,
utils, Relay,
Relay, // Relay API for subscriptions.
} = window.NostrTools; } = window.NostrTools;
// Define relay URLs.
const RELAYS = [ const RELAYS = [
"wss://relay.snort.social", "wss://relay.snort.social",
"wss://relay.damus.io", "wss://relay.damus.io",
"wss://relay.primal.net", "wss://relay.primal.net",
]; ];
// Create a SimplePool instance for publishing events.
const pool = new SimplePool(); const pool = new SimplePool();
// Main form submission handler. document.getElementById("dm-form").addEventListener("submit", async (ev) => {
document ev.preventDefault();
.getElementById("dm-form") clear();
.addEventListener("submit", async (ev) => {
ev.preventDefault();
clear();
try { try {
// 1) Retrieve user input from all fields. // Retrieve user input.
const npub = document.getElementById("npubInput").value.trim(); const npub = document.getElementById("npubInput").value.trim();
if (!npub.startsWith("npub")) { if (!npub.startsWith("npub")) {
throw new Error("Target must start with npub."); throw new Error("Target must start with npub.");
} }
const contactMethod = document const contactMethod = document.getElementById("contactMethod").value.trim();
.getElementById("contactMethod") const videoTitle = document.getElementById("videoTitle").value.trim();
.value.trim(); const magnetLink = document.getElementById("magnetLink").value.trim();
const videoTitle = document const submissionDate = document.getElementById("submissionDate").value.trim();
.getElementById("videoTitle") const reasonBlocked = document.getElementById("reasonBlocked").value.trim();
.value.trim(); const fitsGuidelines = document.getElementById("fitsGuidelines").value.trim();
const magnetLink = document const guidelinesCited = document.getElementById("guidelinesCited").value.trim();
.getElementById("magnetLink") const editedContent = document.getElementById("editedContent").value.trim();
.value.trim(); const changesMade = document.getElementById("changesMade").value.trim();
const submissionDate = document const misunderstanding = document.getElementById("misunderstanding").value.trim();
.getElementById("submissionDate") const externalReferences = document.getElementById("externalReferences").value.trim();
.value.trim(); const signature = document.getElementById("signature").value.trim();
const reasonBlocked = document const declarationDate = document.getElementById("declarationDate").value.trim();
.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();
// 2) Construct the appeal content as a Markdown formatted string. // Construct the appeal content.
const appealContent = ` const appealContent = `
# **bitvid Content Appeals Form** # **bitvid Content Appeals Form**
**1. User Information** **1. User Information**
@@ -347,87 +323,71 @@ By submitting this appeal, you confirm that:
For further questions, reach out through bitvids Nostr support channels. For further questions, reach out through bitvids Nostr support channels.
`.trim(); `.trim();
log("[DEBUG] Constructed appeal content:\n" + appealContent); log("[DEBUG] Constructed appeal content:\n" + appealContent);
// 3) Decode the target npub to get the target public key. // Decode the target npub to get the public key.
log("Decoding target npub..."); log("Decoding target npub...");
const decoded = nip19.decode(npub); const decoded = nip19.decode(npub);
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded)); log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
if (decoded.type !== "npub") { if (decoded.type !== "npub") {
throw new Error("Decoded type is not npub."); throw new Error("Decoded type is not npub.");
}
const targetPubHex = decoded.data;
log("Target pubkey: " + targetPubHex.slice(0, 16) + "...");
// 4) Generate an ephemeral key pair.
log("Generating ephemeral key...");
const ephemeralPriv = generateSecretKey(); // returns a Uint8Array
const ephemeralPubHex = getPublicKey(ephemeralPriv); // returns a hex string
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
// 5) Encrypt the appeal content using NIP04.
log("Encrypting appeal content (nip04)...");
const ciphertext = await nip04.encrypt(
ephemeralPriv,
targetPubHex,
appealContent
);
log("[DEBUG] Ciphertext: " + ciphertext);
log("Encryption done.");
// 6) Build the Nostr event template (kind 4 for DMs) with the encrypted appeal.
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)
);
// 7) Finalize the event: compute event.id, sign it, and assign the pubkey.
const event = finalizeEvent(eventTemplate, ephemeralPriv);
log("[DEBUG] Final event: " + JSON.stringify(event));
// 8) 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");
// 9) For each relay, subscribe using the Relay API to verify that 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 appeal in storage! ID: " +
foundEvent.id.slice(0, 8) +
"...",
"success"
);
}
},
onEose() {
relay.close();
},
});
}
log(
"Done. If the logs show 'Relay accepted' and 'Found our appeal in storage', the appeal was successfully published. A moderator will review your appeal within 7-14 days."
);
} catch (err) {
log("Error: " + err.message, "error");
console.error(err);
} }
}); const targetPubHex = decoded.data;
log("Target 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> </script>
</body> </body>