updated iframe style

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

View File

@@ -3,89 +3,110 @@
<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>
<div class="container">
<div class="form-container">
<h1>bitvid Content Appeals Form</h1> <h1>bitvid Content Appeals Form</h1>
<p> <p>
If you believe your content was unfairly blocked or restricted on bitvid, If you believe your content was unfairly blocked or restricted on bitvid,
@@ -121,36 +142,28 @@
<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 <textarea
id="reasonBlocked" id="reasonBlocked"
rows="3" rows="3"
placeholder="Explain in detail" placeholder="Explain in detail"
></textarea> ></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"> <select id="fitsGuidelines">
<option value="">Select an option</option> <option value="">Select an option</option>
<option value="Yes">Yes</option> <option value="Yes">Yes</option>
<option value="No">No</option> <option value="No">No</option>
</select> </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 <textarea
id="guidelinesCited" id="guidelinesCited"
rows="2" rows="2"
placeholder="Cite the specific guidelines" placeholder="Cite the specific guidelines"
></textarea> ></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"> <select id="editedContent">
<option value="">Select an option</option> <option value="">Select an option</option>
<option value="Yes">Yes</option> <option value="Yes">Yes</option>
@@ -165,18 +178,14 @@
></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 <textarea
id="misunderstanding" id="misunderstanding"
rows="2" rows="2"
placeholder="Provide context" placeholder="Provide context"
></textarea> ></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 <textarea
id="externalReferences" id="externalReferences"
rows="2" rows="2"
@@ -186,10 +195,9 @@
<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" />
@@ -201,6 +209,8 @@
</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,74 +241,42 @@
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
.getElementById("dm-form")
.addEventListener("submit", async (ev) => {
ev.preventDefault(); ev.preventDefault();
clear(); 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**
@@ -349,7 +325,7 @@ For further questions, reach out through bitvids Nostr support channels.
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));
@@ -359,23 +335,19 @@ For further questions, reach out through bitvids Nostr support channels.
const targetPubHex = decoded.data; const targetPubHex = decoded.data;
log("Target pubkey: " + targetPubHex.slice(0, 16) + "..."); log("Target pubkey: " + targetPubHex.slice(0, 16) + "...");
// 4) Generate an ephemeral key pair. // Generate an ephemeral key pair.
log("Generating ephemeral key..."); log("Generating ephemeral key...");
const ephemeralPriv = generateSecretKey(); // returns a Uint8Array const ephemeralPriv = generateSecretKey();
const ephemeralPubHex = getPublicKey(ephemeralPriv); // returns a hex string const ephemeralPubHex = getPublicKey(ephemeralPriv);
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "..."); log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
// 5) Encrypt the appeal content using NIP04. // Encrypt the appeal content.
log("Encrypting appeal content (nip04)..."); log("Encrypting appeal content (nip04)...");
const ciphertext = await nip04.encrypt( const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, appealContent);
ephemeralPriv,
targetPubHex,
appealContent
);
log("[DEBUG] Ciphertext: " + ciphertext); log("[DEBUG] Ciphertext: " + ciphertext);
log("Encryption done."); log("Encryption done.");
// 6) Build the Nostr event template (kind 4 for DMs) with the encrypted appeal. // Build the event template.
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
const eventTemplate = { const eventTemplate = {
kind: 4, kind: 4,
@@ -383,35 +355,25 @@ For further questions, reach out through bitvids Nostr support channels.
tags: [["p", targetPubHex]], tags: [["p", targetPubHex]],
content: ciphertext, content: ciphertext,
}; };
log( log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
"[DEBUG] Event template before finalizing: " +
JSON.stringify(eventTemplate)
);
// 7) Finalize the event: compute event.id, sign it, and assign the pubkey. // Finalize the event.
const event = finalizeEvent(eventTemplate, ephemeralPriv); const event = finalizeEvent(eventTemplate, ephemeralPriv);
log("[DEBUG] Final event: " + JSON.stringify(event)); log("[DEBUG] Final event: " + JSON.stringify(event));
// 8) Publish the event to all relays. // Publish the event to all relays.
log("Publishing the appeal to relays..."); log("Publishing the appeal to relays...");
await Promise.any(pool.publish(RELAYS, event)); await Promise.any(pool.publish(RELAYS, event));
log("At least one relay accepted the event.", "success"); log("At least one relay accepted the event.", "success");
// 9) For each relay, subscribe using the Relay API to verify that the event appears. // Subscribe to each relay.
for (const url of RELAYS) { for (const url of RELAYS) {
log("Connecting to " + url + " for subscription..."); log("Connecting to " + url + " for subscription...");
const relay = await Relay.connect(url); const relay = await Relay.connect(url);
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], { relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
onEvent(foundEvent) { onEvent(foundEvent) {
if (foundEvent.id === event.id) { if (foundEvent.id === event.id) {
log( log("[" + url + "] => Found our appeal in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
"[" +
url +
"] => Found our appeal in storage! ID: " +
foundEvent.id.slice(0, 8) +
"...",
"success"
);
} }
}, },
onEose() { onEose() {
@@ -420,9 +382,7 @@ For further questions, reach out through bitvids Nostr support channels.
}); });
} }
log( 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.");
"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) { } catch (err) {
log("Error: " + err.message, "error"); log("Error: " + err.message, "error");
console.error(err); console.error(err);