mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2025-09-08 23:18:43 +00:00
update
This commit is contained in:
@@ -4,32 +4,32 @@
|
|||||||
"description": "seed. zap. subscribe.",
|
"description": "seed. zap. subscribe.",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "assets/png/android-chrome-192x192.png",
|
"src": "src/assets/png/android-chrome-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "assets/png/android-chrome-512x512.png",
|
"src": "src/assets/png/android-chrome-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "assets/png/apple-touch-icon.png",
|
"src": "src/assets/png/apple-touch-icon.png",
|
||||||
"sizes": "180x180",
|
"sizes": "180x180",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "assets/png/favicon-32x32.png",
|
"src": "src/assets/png/favicon-32x32.png",
|
||||||
"sizes": "32x32",
|
"sizes": "32x32",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "assets/png/favicon-16x16.png",
|
"src": "src/assets/png/favicon-16x16.png",
|
||||||
"sizes": "16x16",
|
"sizes": "16x16",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"start_url": "/index.html",
|
"start_url": "src/index.html",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0f172a",
|
"background_color": "#0f172a",
|
||||||
"theme_color": "#0f172a",
|
"theme_color": "#0f172a",
|
@@ -67,7 +67,7 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="w-full" style="height: 80vh">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://beta.bitvid.network/components/iframe_forms/iframe-application-form.html"
|
src="/src/components/iframe_forms/iframe-application-form.html"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="w-full" style="height: 80vh">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://beta.bitvid.network/components/iframe_forms/iframe-bug-fix-form.html"
|
src="/src/components/iframe_forms/iframe-bug-fix-form.html"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
@@ -53,7 +53,7 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="w-full" style="height: 80vh">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://beta.bitvid.network/components/iframe_forms/iframe-content-appeals-form.html"
|
src="/src/components/iframe_forms/iframe-content-appeals-form.html"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
@@ -53,7 +53,7 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="w-full" style="height: 80vh">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://beta.bitvid.network/components/iframe_forms/iframe-request-form.html"
|
src="/src/components/iframe_forms/iframe-request-form.html"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
@@ -57,7 +57,7 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="w-full" style="height: 80vh">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://beta.bitvid.network/components/iframe_forms/iframe-feedback-form.html"
|
src="/src/components/iframe_forms/iframe-feedback-form.html"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>bitvid Whitelist Application Form</title>
|
<title>bitvid Whitelist Application Form</title>
|
||||||
<!-- Link to your main stylesheet -->
|
<!-- Link to your main stylesheet -->
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="../../css/style.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Override for form page to match modal field styling */
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input, textarea, select mimic modal field styles */
|
/* Input, textarea, select mimic modal field styles */
|
||||||
@@ -51,8 +51,8 @@
|
|||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||||
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.375rem; /* rounded-md */
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #3B82F6; /* blue-500 */
|
border-color: #3b82f6; /* blue-500 */
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 1px #3B82F6;
|
box-shadow: 0 0 0 1px #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style for checkboxes – display inline with a label */
|
/* Style for checkboxes – display inline with a label */
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
/* Button styled similarly to modal publish button */
|
/* Button styled similarly to modal publish button */
|
||||||
button {
|
button {
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: #3B82F6; /* blue-500 */
|
background: #3b82f6; /* blue-500 */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -97,13 +97,13 @@
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: #F87171; /* a red tint */
|
color: #f87171; /* a red tint */
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
color: #3B82F6; /* blue-500 */
|
color: #3b82f6; /* blue-500 */
|
||||||
}
|
}
|
||||||
.warn {
|
.warn {
|
||||||
color: #FACC15; /* a yellow tone */
|
color: #facc15; /* a yellow tone */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Scrollbar styling for WebKit browsers */
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
@@ -115,13 +115,13 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #3B82F6;
|
background-color: #3b82f6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
/* Custom Scrollbar styling for Firefox */
|
/* Custom Scrollbar styling for Firefox */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3B82F6 transparent;
|
scrollbar-color: #3b82f6 transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Load nostr‑tools v2.10.4 -->
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
@@ -131,72 +131,163 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<p>
|
<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.
|
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>
|
</p>
|
||||||
<form id="wl-form">
|
<form id="wl-form">
|
||||||
<!-- 1. Applicant Information -->
|
<!-- 1. Applicant Information -->
|
||||||
<h2>1. Applicant Information</h2>
|
<h2>1. Applicant Information</h2>
|
||||||
<label for="applicantNpub">Nostr Public Key (npub):</label>
|
<label for="applicantNpub">Nostr Public Key (npub):</label>
|
||||||
<input type="text" id="applicantNpub" placeholder="Enter your npub" required />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="applicantNpub"
|
||||||
|
placeholder="Enter your npub"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
<label for="contactMethod">Preferred Contact Method (if applicable):</label>
|
<label for="contactMethod"
|
||||||
<input type="text" id="contactMethod" placeholder="Nostr DM, email, or other" />
|
>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>
|
<label for="username">Username or Alias (if applicable):</label>
|
||||||
<input type="text" id="username" placeholder="Enter your preferred name" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
placeholder="Enter your preferred name"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 2. Content Intent -->
|
<!-- 2. Content Intent -->
|
||||||
<h2>2. Content Intent</h2>
|
<h2>2. Content Intent</h2>
|
||||||
<p>What type of content do you plan to upload? (Check all that apply)</p>
|
<p>
|
||||||
|
What type of content do you plan to upload? (Check all that apply)
|
||||||
|
</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="contentType" value="Educational" /> Educational</label>
|
<label
|
||||||
<label><input type="checkbox" name="contentType" value="Entertainment" /> Entertainment</label>
|
><input type="checkbox" name="contentType" value="Educational" />
|
||||||
<label><input type="checkbox" name="contentType" value="News & Journalism" /> News & Journalism</label>
|
Educational</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
|
||||||
<label><input type="checkbox" name="contentType" value="Other" /> Other</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>
|
</div>
|
||||||
<label for="otherContent">Other (please specify):</label>
|
<label for="otherContent">Other (please specify):</label>
|
||||||
<input type="text" id="otherContent" placeholder="Describe if Other was selected" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="otherContent"
|
||||||
|
placeholder="Describe if Other was selected"
|
||||||
|
/>
|
||||||
|
|
||||||
<label for="whyJoin">Why do you want to join bitvid?</label>
|
<label for="whyJoin">Why do you want to join bitvid?</label>
|
||||||
<textarea id="whyJoin" rows="3" placeholder="Explain your motivation"></textarea>
|
<textarea
|
||||||
|
id="whyJoin"
|
||||||
|
rows="3"
|
||||||
|
placeholder="Explain your motivation"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
<label for="priorExperience">Have you created content on other platforms before?</label>
|
<label for="priorExperience"
|
||||||
|
>Have you created content on other platforms before?</label
|
||||||
|
>
|
||||||
<select id="priorExperience">
|
<select id="priorExperience">
|
||||||
<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="experienceLinks">If yes, provide links or references to your previous work:</label>
|
<label for="experienceLinks"
|
||||||
<textarea id="experienceLinks" rows="2" placeholder="Paste links or describe your work"></textarea>
|
>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 -->
|
<!-- 3. Community Engagement -->
|
||||||
<h2>3. Community Engagement</h2>
|
<h2>3. Community Engagement</h2>
|
||||||
<label for="familiarGuidelines">Are you familiar with bitvid’s Community Guidelines?</label>
|
<label for="familiarGuidelines"
|
||||||
|
>Are you familiar with bitvid’s Community Guidelines?</label
|
||||||
|
>
|
||||||
<select id="familiarGuidelines">
|
<select id="familiarGuidelines">
|
||||||
<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="agreeGuidelines">Do you agree to follow the guidelines and respect decentralized moderation?</label>
|
<label for="agreeGuidelines"
|
||||||
|
>Do you agree to follow the guidelines and respect decentralized
|
||||||
|
moderation?</label
|
||||||
|
>
|
||||||
<select id="agreeGuidelines">
|
<select id="agreeGuidelines">
|
||||||
<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="communityContribution">How do you plan to contribute positively to the bitvid community?</label>
|
<label for="communityContribution"
|
||||||
<textarea id="communityContribution" rows="3" placeholder="Explain your approach"></textarea>
|
>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 -->
|
<!-- 4. Additional Information -->
|
||||||
<h2>4. Additional Information</h2>
|
<h2>4. Additional Information</h2>
|
||||||
<label for="specialSkills">Do you have any special skills or interests that could help improve bitvid?</label>
|
<label for="specialSkills"
|
||||||
<textarea id="specialSkills" rows="3" placeholder="e.g., software development, moderation, design, advocacy"></textarea>
|
>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>
|
<label for="testFeatures"
|
||||||
|
>Would you be interested in helping test new features?</label
|
||||||
|
>
|
||||||
<select id="testFeatures">
|
<select id="testFeatures">
|
||||||
<option value="">Select an option</option>
|
<option value="">Select an option</option>
|
||||||
<option value="Yes">Yes</option>
|
<option value="Yes">Yes</option>
|
||||||
@@ -207,9 +298,10 @@
|
|||||||
<h2>5. Declaration</h2>
|
<h2>5. Declaration</h2>
|
||||||
<p>
|
<p>
|
||||||
By submitting this application, you confirm that:
|
By submitting this application, you confirm that:
|
||||||
<br />- The information provided is accurate.
|
<br />- The information provided is accurate. <br />- You understand
|
||||||
<br />- You understand that approval is based on community alignment and available capacity.
|
that approval is based on community alignment and available
|
||||||
<br />- You acknowledge that whitelist status may be revoked if guidelines are violated.
|
capacity. <br />- You acknowledge that whitelist status may be
|
||||||
|
revoked if guidelines are violated.
|
||||||
</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" />
|
||||||
@@ -242,53 +334,90 @@
|
|||||||
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
Relay,
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
// Set the recipient's NPUB (your personal NPUB) here.
|
// Set the recipient's NPUB (your personal NPUB) here.
|
||||||
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
const recipientNpub =
|
||||||
|
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
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",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
document.getElementById("wl-form").addEventListener("submit", async (ev) => {
|
document
|
||||||
ev.preventDefault();
|
.getElementById("wl-form")
|
||||||
clear();
|
.addEventListener("submit", async (ev) => {
|
||||||
try {
|
ev.preventDefault();
|
||||||
// Retrieve applicant-provided input.
|
clear();
|
||||||
const applicantNpub = document.getElementById("applicantNpub").value.trim();
|
try {
|
||||||
const contactMethod = document.getElementById("contactMethod").value.trim();
|
// Retrieve applicant-provided input.
|
||||||
const username = document.getElementById("username").value.trim();
|
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
|
// Section 2: Content Intent
|
||||||
// Get all checked content types
|
// Get all checked content types
|
||||||
const contentTypeNodes = document.querySelectorAll('input[name="contentType"]:checked');
|
const contentTypeNodes = document.querySelectorAll(
|
||||||
let contentTypes = [];
|
'input[name="contentType"]:checked'
|
||||||
contentTypeNodes.forEach((node) => {
|
);
|
||||||
contentTypes.push(node.value);
|
let contentTypes = [];
|
||||||
});
|
contentTypeNodes.forEach((node) => {
|
||||||
const otherContent = document.getElementById("otherContent").value.trim();
|
contentTypes.push(node.value);
|
||||||
const whyJoin = document.getElementById("whyJoin").value.trim();
|
});
|
||||||
const priorExperience = document.getElementById("priorExperience").value.trim();
|
const otherContent = document
|
||||||
const experienceLinks = document.getElementById("experienceLinks").value.trim();
|
.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
|
// Section 3: Community Engagement
|
||||||
const familiarGuidelines = document.getElementById("familiarGuidelines").value.trim();
|
const familiarGuidelines = document
|
||||||
const agreeGuidelines = document.getElementById("agreeGuidelines").value.trim();
|
.getElementById("familiarGuidelines")
|
||||||
const communityContribution = document.getElementById("communityContribution").value.trim();
|
.value.trim();
|
||||||
|
const agreeGuidelines = document
|
||||||
|
.getElementById("agreeGuidelines")
|
||||||
|
.value.trim();
|
||||||
|
const communityContribution = document
|
||||||
|
.getElementById("communityContribution")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Section 4: Additional Information
|
// Section 4: Additional Information
|
||||||
const specialSkills = document.getElementById("specialSkills").value.trim();
|
const specialSkills = document
|
||||||
const testFeatures = document.getElementById("testFeatures").value.trim();
|
.getElementById("specialSkills")
|
||||||
|
.value.trim();
|
||||||
|
const testFeatures = document
|
||||||
|
.getElementById("testFeatures")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Section 5: Declaration
|
// Section 5: Declaration
|
||||||
const signature = document.getElementById("signature").value.trim();
|
const signature = document
|
||||||
const declarationDate = document.getElementById("declarationDate").value.trim();
|
.getElementById("signature")
|
||||||
|
.value.trim();
|
||||||
|
const declarationDate = document
|
||||||
|
.getElementById("declarationDate")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Construct the whitelist application content.
|
// Construct the whitelist application content.
|
||||||
const applicationContent = `
|
const applicationContent = `
|
||||||
# **bitvid Whitelist Application Form**
|
# **bitvid Whitelist Application Form**
|
||||||
|
|
||||||
**1. Applicant Information**
|
**1. Applicant Information**
|
||||||
@@ -301,13 +430,19 @@
|
|||||||
- **Other (if applicable):** ${otherContent || "N/A"}
|
- **Other (if applicable):** ${otherContent || "N/A"}
|
||||||
- **Why do you want to join bitvid?**
|
- **Why do you want to join bitvid?**
|
||||||
${whyJoin || "N/A"}
|
${whyJoin || "N/A"}
|
||||||
- **Have you created content on other platforms before?** ${priorExperience || "N/A"}
|
- **Have you created content on other platforms before?** ${
|
||||||
|
priorExperience || "N/A"
|
||||||
|
}
|
||||||
- **Links or references to your previous work:**
|
- **Links or references to your previous work:**
|
||||||
${experienceLinks || "N/A"}
|
${experienceLinks || "N/A"}
|
||||||
|
|
||||||
**3. Community Engagement**
|
**3. Community Engagement**
|
||||||
- **Familiar with bitvid’s Community Guidelines?** ${familiarGuidelines || "N/A"}
|
- **Familiar with bitvid’s Community Guidelines?** ${
|
||||||
- **Agree to follow guidelines and respect moderation?** ${agreeGuidelines || "N/A"}
|
familiarGuidelines || "N/A"
|
||||||
|
}
|
||||||
|
- **Agree to follow guidelines and respect moderation?** ${
|
||||||
|
agreeGuidelines || "N/A"
|
||||||
|
}
|
||||||
- **How do you plan to contribute to the community?**
|
- **How do you plan to contribute to the community?**
|
||||||
${communityContribution || "N/A"}
|
${communityContribution || "N/A"}
|
||||||
|
|
||||||
@@ -332,71 +467,90 @@ By submitting this application, you confirm that:
|
|||||||
For further questions, contact us through bitvid’s Nostr support channels.
|
For further questions, contact us through bitvid’s Nostr support channels.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
log("[DEBUG] Constructed application content:\n" + applicationContent);
|
log(
|
||||||
|
"[DEBUG] Constructed application content:\n" +
|
||||||
|
applicationContent
|
||||||
|
);
|
||||||
|
|
||||||
// Decode the recipient NPUB to get the public key.
|
// Decode the recipient NPUB to get the public key.
|
||||||
log("Decoding recipient npub...");
|
log("Decoding recipient npub...");
|
||||||
const decoded = nip19.decode(recipientNpub);
|
const decoded = nip19.decode(recipientNpub);
|
||||||
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("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);
|
||||||
}
|
}
|
||||||
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>bitvid Bug Report Form</title>
|
<title>bitvid Bug Report Form</title>
|
||||||
<!-- Link to your main stylesheet -->
|
<!-- Link to your main stylesheet -->
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="../../css/style.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Override for form page to match modal field styling */
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input, textarea, and select mimic modal field styles */
|
/* Input, textarea, and select mimic modal field styles */
|
||||||
@@ -51,8 +51,8 @@
|
|||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||||
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.375rem; /* rounded-md */
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #3B82F6; /* blue-500 */
|
border-color: #3b82f6; /* blue-500 */
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 1px #3B82F6;
|
box-shadow: 0 0 0 1px #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkbox group styling */
|
/* Checkbox group styling */
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
/* Button styled similarly to modal publish button */
|
/* Button styled similarly to modal publish button */
|
||||||
button {
|
button {
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: #3B82F6; /* blue-500 */
|
background: #3b82f6; /* blue-500 */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -97,13 +97,13 @@
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: #F87171; /* a red tint */
|
color: #f87171; /* a red tint */
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
color: #3B82F6; /* blue-500 */
|
color: #3b82f6; /* blue-500 */
|
||||||
}
|
}
|
||||||
.warn {
|
.warn {
|
||||||
color: #FACC15; /* a yellow tone */
|
color: #facc15; /* a yellow tone */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Scrollbar styling for WebKit browsers */
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
@@ -115,13 +115,13 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #3B82F6;
|
background-color: #3b82f6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
/* Custom Scrollbar styling for Firefox */
|
/* Custom Scrollbar styling for Firefox */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3B82F6 transparent;
|
scrollbar-color: #3b82f6 transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Load nostr‑tools v2.10.4 -->
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
@@ -131,44 +131,99 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<p>
|
<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.
|
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>
|
</p>
|
||||||
<form id="bug-form">
|
<form id="bug-form">
|
||||||
<!-- Section 1: User Information -->
|
<!-- Section 1: User Information -->
|
||||||
<h2>1. User Information</h2>
|
<h2>1. User Information</h2>
|
||||||
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="userNpub"
|
||||||
|
placeholder="Enter your npub (optional)"
|
||||||
|
/>
|
||||||
<p>Are you a (check all that apply):</p>
|
<p>Are you a (check all that apply):</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
<label
|
||||||
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
><input type="checkbox" name="userRole" value="Viewer" />
|
||||||
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
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>
|
</div>
|
||||||
|
|
||||||
<!-- Section 2: Bug Details -->
|
<!-- Section 2: Bug Details -->
|
||||||
<h2>2. Bug Details</h2>
|
<h2>2. Bug Details</h2>
|
||||||
<label for="issueDescription">Describe the issue:</label>
|
<label for="issueDescription">Describe the issue:</label>
|
||||||
<textarea id="issueDescription" rows="3" placeholder="Provide a clear and concise description of the problem"></textarea>
|
<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>
|
<label for="stepsToReproduce">Steps to reproduce the bug:</label>
|
||||||
<textarea id="stepsToReproduce" rows="3" placeholder="List the actions taken before encountering the issue"></textarea>
|
<textarea
|
||||||
|
id="stepsToReproduce"
|
||||||
|
rows="3"
|
||||||
|
placeholder="List the actions taken before encountering the issue"
|
||||||
|
></textarea>
|
||||||
<label for="expectedBehavior">Expected behavior:</label>
|
<label for="expectedBehavior">Expected behavior:</label>
|
||||||
<textarea id="expectedBehavior" rows="2" placeholder="What should have happened instead?"></textarea>
|
<textarea
|
||||||
|
id="expectedBehavior"
|
||||||
|
rows="2"
|
||||||
|
placeholder="What should have happened instead?"
|
||||||
|
></textarea>
|
||||||
<label for="actualBehavior">Actual behavior:</label>
|
<label for="actualBehavior">Actual behavior:</label>
|
||||||
<textarea id="actualBehavior" rows="2" placeholder="What actually happened?"></textarea>
|
<textarea
|
||||||
|
id="actualBehavior"
|
||||||
|
rows="2"
|
||||||
|
placeholder="What actually happened?"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
<!-- Section 3: Device & Environment -->
|
<!-- Section 3: Device & Environment -->
|
||||||
<h2>3. Device & Environment</h2>
|
<h2>3. Device & Environment</h2>
|
||||||
<p>What device were you using? (check all that apply):</p>
|
<p>What device were you using? (check all that apply):</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="deviceUsed" value="Desktop" /> Desktop</label>
|
<label
|
||||||
<label><input type="checkbox" name="deviceUsed" value="Mobile" /> Mobile</label>
|
><input type="checkbox" name="deviceUsed" value="Desktop" />
|
||||||
<label><input type="checkbox" name="deviceUsed" value="Tablet" /> Tablet</label>
|
Desktop</label
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" name="deviceUsed" value="Mobile" />
|
||||||
|
Mobile</label
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" name="deviceUsed" value="Tablet" />
|
||||||
|
Tablet</label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<label for="operatingSystem">Operating System:</label>
|
<label for="operatingSystem">Operating System:</label>
|
||||||
<input type="text" id="operatingSystem" placeholder="e.g., Windows, macOS, Linux, iOS, Android" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="operatingSystem"
|
||||||
|
placeholder="e.g., Windows, macOS, Linux, iOS, Android"
|
||||||
|
/>
|
||||||
<label for="browserInfo">Browser (if applicable):</label>
|
<label for="browserInfo">Browser (if applicable):</label>
|
||||||
<input type="text" id="browserInfo" placeholder="e.g., Chrome, Firefox, Safari" />
|
<input
|
||||||
<label for="usingVPN">Are you using a VPN or privacy-focused browser settings?</label>
|
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">
|
<select id="usingVPN">
|
||||||
<option value="">Select an option</option>
|
<option value="">Select an option</option>
|
||||||
<option value="Yes">Yes</option>
|
<option value="Yes">Yes</option>
|
||||||
@@ -177,12 +232,28 @@
|
|||||||
|
|
||||||
<!-- Section 4: Screenshots or Logs -->
|
<!-- Section 4: Screenshots or Logs -->
|
||||||
<h2>4. Screenshots or Logs (If Available)</h2>
|
<h2>4. Screenshots or Logs (If Available)</h2>
|
||||||
<label for="screenshotInfo">Can you provide a screenshot or screen recording?</label>
|
<label for="screenshotInfo"
|
||||||
<textarea id="screenshotInfo" rows="2" placeholder="Attach if possible (e.g., URL to screenshot)"></textarea>
|
>Can you provide a screenshot or screen recording?</label
|
||||||
<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>
|
<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>
|
<label for="consoleLogs">Any relevant console logs?</label>
|
||||||
<textarea id="consoleLogs" rows="2" placeholder="Paste any browser console logs if available"></textarea>
|
<textarea
|
||||||
|
id="consoleLogs"
|
||||||
|
rows="2"
|
||||||
|
placeholder="Paste any browser console logs if available"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
<!-- Section 5: Additional Information -->
|
<!-- Section 5: Additional Information -->
|
||||||
<h2>5. Additional Information</h2>
|
<h2>5. Additional Information</h2>
|
||||||
@@ -193,14 +264,23 @@
|
|||||||
<option value="Occasionally">Occasionally</option>
|
<option value="Occasionally">Occasionally</option>
|
||||||
<option value="Rarely">Rarely</option>
|
<option value="Rarely">Rarely</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="impactCore">Does this bug impact core functionality (e.g., video playback, uploads, etc.)?</label>
|
<label for="impactCore"
|
||||||
|
>Does this bug impact core functionality (e.g., video playback,
|
||||||
|
uploads, etc.)?</label
|
||||||
|
>
|
||||||
<select id="impactCore">
|
<select id="impactCore">
|
||||||
<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="additionalNotes">Any additional notes or suggestions?</label>
|
<label for="additionalNotes"
|
||||||
<textarea id="additionalNotes" rows="3" placeholder="Describe any other relevant details"></textarea>
|
>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>
|
<button type="submit">Submit Bug Report</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -234,58 +314,89 @@
|
|||||||
nip04,
|
nip04,
|
||||||
nip19,
|
nip19,
|
||||||
SimplePool,
|
SimplePool,
|
||||||
Relay
|
Relay,
|
||||||
} = window.NostrTools;
|
} = window.NostrTools;
|
||||||
|
|
||||||
// Set the recipient's NPUB (your personal NPUB)
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
const recipientNpub =
|
||||||
|
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
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",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
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
|
document
|
||||||
const issueDescription = document.getElementById("issueDescription").value.trim();
|
.getElementById("bug-form")
|
||||||
const stepsToReproduce = document.getElementById("stepsToReproduce").value.trim();
|
.addEventListener("submit", async (ev) => {
|
||||||
const expectedBehavior = document.getElementById("expectedBehavior").value.trim();
|
ev.preventDefault();
|
||||||
const actualBehavior = document.getElementById("actualBehavior").value.trim();
|
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 3: Device & Environment
|
// Section 2: Bug Details
|
||||||
const deviceNodes = document.querySelectorAll('input[name="deviceUsed"]:checked');
|
const issueDescription = document
|
||||||
let devicesUsed = [];
|
.getElementById("issueDescription")
|
||||||
deviceNodes.forEach(node => {
|
.value.trim();
|
||||||
devicesUsed.push(node.value);
|
const stepsToReproduce = document
|
||||||
});
|
.getElementById("stepsToReproduce")
|
||||||
const operatingSystem = document.getElementById("operatingSystem").value.trim();
|
.value.trim();
|
||||||
const browserInfo = document.getElementById("browserInfo").value.trim();
|
const expectedBehavior = document
|
||||||
const usingVPN = document.getElementById("usingVPN").value.trim();
|
.getElementById("expectedBehavior")
|
||||||
|
.value.trim();
|
||||||
|
const actualBehavior = document
|
||||||
|
.getElementById("actualBehavior")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Section 4: Screenshots or Logs
|
// Section 3: Device & Environment
|
||||||
const screenshotInfo = document.getElementById("screenshotInfo").value.trim();
|
const deviceNodes = document.querySelectorAll(
|
||||||
const errorMessages = document.getElementById("errorMessages").value.trim();
|
'input[name="deviceUsed"]:checked'
|
||||||
const consoleLogs = document.getElementById("consoleLogs").value.trim();
|
);
|
||||||
|
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 5: Additional Information
|
// Section 4: Screenshots or Logs
|
||||||
const bugFrequency = document.getElementById("bugFrequency").value.trim();
|
const screenshotInfo = document
|
||||||
const impactCore = document.getElementById("impactCore").value.trim();
|
.getElementById("screenshotInfo")
|
||||||
const additionalNotes = document.getElementById("additionalNotes").value.trim();
|
.value.trim();
|
||||||
|
const errorMessages = document
|
||||||
|
.getElementById("errorMessages")
|
||||||
|
.value.trim();
|
||||||
|
const consoleLogs = document
|
||||||
|
.getElementById("consoleLogs")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Construct the bug report content as Markdown.
|
// Section 5: Additional Information
|
||||||
const bugReportContent = `
|
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**
|
# **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.
|
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.
|
||||||
@@ -328,71 +439,89 @@ Our team reviews bug reports regularly. While we aim to fix critical issues as s
|
|||||||
For urgent issues, contact us through bitvid’s Nostr support channels.
|
For urgent issues, contact us through bitvid’s Nostr support channels.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
log("[DEBUG] Constructed bug report content:\n" + bugReportContent);
|
log(
|
||||||
|
"[DEBUG] Constructed bug report content:\n" + bugReportContent
|
||||||
|
);
|
||||||
|
|
||||||
// Decode the recipient NPUB to get the public key.
|
// Decode the recipient NPUB to get the public key.
|
||||||
log("Decoding recipient npub...");
|
log("Decoding recipient npub...");
|
||||||
const decoded = nip19.decode(recipientNpub);
|
const decoded = nip19.decode(recipientNpub);
|
||||||
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("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);
|
||||||
}
|
}
|
||||||
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -4,10 +4,10 @@
|
|||||||
<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 to your main stylesheet -->
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="../../css/style.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Override for form page to match modal field styling */
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
/* Remove width constraints from body so our container can be full width */
|
/* Remove width constraints from body so our container can be full width */
|
||||||
body {
|
body {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
font-family: system-ui, -apple-system, sans-serif;
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override the .container to use the full available width */
|
/* Override the .container to use the full available width */
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card-like container for the form, similar to modal-content */
|
/* Card-like container for the form, similar to modal-content */
|
||||||
.form-container {
|
.form-container {
|
||||||
background-color: #111827; /* Tailwind's bg-gray-900 */
|
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||||
@@ -31,52 +31,52 @@
|
|||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Labels in a light gray */
|
/* Labels in a light gray */
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input, textarea, and select mimic modal field styles */
|
/* Input, textarea, and select mimic modal field styles */
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||||
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.375rem; /* rounded-md */
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #3B82F6; /* blue-500 */
|
border-color: #3b82f6; /* blue-500 */
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 1px #3B82F6;
|
box-shadow: 0 0 0 1px #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button styled similarly to modal publish button */
|
/* Button styled similarly to modal publish button */
|
||||||
button {
|
button {
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: #3B82F6; /* blue-500 */
|
background: #3b82f6; /* blue-500 */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status log area */
|
/* Status log area */
|
||||||
#status {
|
#status {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
@@ -90,15 +90,15 @@
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: #F87171; /* a red tint */
|
color: #f87171; /* a red tint */
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
color: #3B82F6; /* blue-500 */
|
color: #3b82f6; /* blue-500 */
|
||||||
}
|
}
|
||||||
.warn {
|
.warn {
|
||||||
color: #FACC15; /* a yellow tone */
|
color: #facc15; /* a yellow tone */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Scrollbar styling for WebKit browsers */
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@@ -108,13 +108,13 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #3B82F6;
|
background-color: #3b82f6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
/* Custom Scrollbar styling for Firefox */
|
/* Custom Scrollbar styling for Firefox */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3B82F6 transparent;
|
scrollbar-color: #3b82f6 transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Load nostr‑tools v2.10.4 -->
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
@@ -124,70 +124,115 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<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
|
||||||
please complete this form. Appeals will be reviewed manually, and
|
bitvid, please complete this form. Appeals will be reviewed manually,
|
||||||
decisions will be communicated back to you.
|
and decisions will be communicated back to you.
|
||||||
</p>
|
</p>
|
||||||
<form id="dm-form">
|
<form id="dm-form">
|
||||||
<!-- NPUB input removed as the recipient is now set by the code -->
|
<!-- NPUB input removed as the recipient is now set by the code -->
|
||||||
<h2>1. User Information</h2>
|
<h2>1. User Information</h2>
|
||||||
<label for="contactMethod">Contact Method (if applicable):</label>
|
<label for="contactMethod">Contact Method (if applicable):</label>
|
||||||
<input type="text" id="contactMethod" placeholder="Nostr DM, email, or other" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="contactMethod"
|
||||||
|
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">Why do you believe your content was unfairly blocked?</label>
|
<label for="reasonBlocked"
|
||||||
<textarea id="reasonBlocked" rows="3" placeholder="Explain in detail"></textarea>
|
>Why do you believe your content was unfairly blocked?</label
|
||||||
|
>
|
||||||
<label for="fitsGuidelines">Does your content fit within bitvid's Community Guidelines?</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">
|
<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">If yes, which guideline(s) support your appeal?</label>
|
<label for="guidelinesCited"
|
||||||
<textarea id="guidelinesCited" rows="2" placeholder="Cite the specific guidelines"></textarea>
|
>If yes, which guideline(s) support your appeal?</label
|
||||||
|
>
|
||||||
<label for="editedContent">Was this content edited after being blocked?</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">
|
<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>
|
||||||
<option value="No">No</option>
|
<option value="No">No</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="changesMade">If yes, what changes were made?</label>
|
<label for="changesMade">If yes, what changes were made?</label>
|
||||||
<textarea id="changesMade" rows="2" placeholder="Describe the modifications"></textarea>
|
<textarea
|
||||||
|
id="changesMade"
|
||||||
|
rows="2"
|
||||||
|
placeholder="Describe the modifications"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
<h2>4. Additional Context</h2>
|
<h2>4. Additional Context</h2>
|
||||||
<label for="misunderstanding">Was there any misunderstanding or misclassification?</label>
|
<label for="misunderstanding"
|
||||||
<textarea id="misunderstanding" rows="2" placeholder="Provide context"></textarea>
|
>Was there any misunderstanding or misclassification?</label
|
||||||
|
>
|
||||||
<label for="externalReferences">Are there external references that validate your appeal?</label>
|
<textarea
|
||||||
<textarea id="externalReferences" rows="2" placeholder="Links, citations, or additional info"></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>
|
<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 content.
|
<br />- You are the original creator or authorized representative of
|
||||||
<br />- Your appeal is submitted in good faith and aligns with bitvid’s policies.
|
the content. <br />- Your appeal is submitted in good faith and
|
||||||
<br />- You understand that final decisions are at the discretion of bitvid’s moderation process.
|
aligns with bitvid’s policies. <br />- You understand that final
|
||||||
|
decisions are at the discretion of bitvid’s 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>
|
||||||
@@ -213,41 +258,78 @@
|
|||||||
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
Relay,
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
// Set the recipient's NPUB (your personal NPUB) here.
|
// Set the recipient's NPUB (your personal NPUB) here.
|
||||||
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
const recipientNpub =
|
||||||
|
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
|
||||||
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",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
document.getElementById("dm-form").addEventListener("submit", async (ev) => {
|
document
|
||||||
ev.preventDefault();
|
.getElementById("dm-form")
|
||||||
clear();
|
.addEventListener("submit", async (ev) => {
|
||||||
try {
|
ev.preventDefault();
|
||||||
// Note: The customer's NPUB input is removed.
|
clear();
|
||||||
// Use the recipientNpub constant to get the public key.
|
try {
|
||||||
|
// Note: The customer's NPUB input is removed.
|
||||||
const contactMethod = document.getElementById("contactMethod").value.trim();
|
// Use the recipientNpub constant to get the public key.
|
||||||
const videoTitle = document.getElementById("videoTitle").value.trim();
|
|
||||||
const magnetLink = document.getElementById("magnetLink").value.trim();
|
const contactMethod = document
|
||||||
const submissionDate = document.getElementById("submissionDate").value.trim();
|
.getElementById("contactMethod")
|
||||||
const reasonBlocked = document.getElementById("reasonBlocked").value.trim();
|
.value.trim();
|
||||||
const fitsGuidelines = document.getElementById("fitsGuidelines").value.trim();
|
const videoTitle = document
|
||||||
const guidelinesCited = document.getElementById("guidelinesCited").value.trim();
|
.getElementById("videoTitle")
|
||||||
const editedContent = document.getElementById("editedContent").value.trim();
|
.value.trim();
|
||||||
const changesMade = document.getElementById("changesMade").value.trim();
|
const magnetLink = document
|
||||||
const misunderstanding = document.getElementById("misunderstanding").value.trim();
|
.getElementById("magnetLink")
|
||||||
const externalReferences = document.getElementById("externalReferences").value.trim();
|
.value.trim();
|
||||||
const signature = document.getElementById("signature").value.trim();
|
const submissionDate = document
|
||||||
const declarationDate = document.getElementById("declarationDate").value.trim();
|
.getElementById("submissionDate")
|
||||||
|
.value.trim();
|
||||||
// Construct the appeal content.
|
const reasonBlocked = document
|
||||||
const appealContent = `
|
.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**
|
# **bitvid Content Appeals Form**
|
||||||
|
|
||||||
**1. User Information**
|
**1. User Information**
|
||||||
@@ -291,72 +373,88 @@ By submitting this appeal, you confirm that:
|
|||||||
|
|
||||||
For further questions, reach out through bitvid’s Nostr support channels.
|
For further questions, reach out through bitvid’s Nostr support channels.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
log("[DEBUG] Constructed appeal content:\n" + appealContent);
|
log("[DEBUG] Constructed appeal content:\n" + appealContent);
|
||||||
|
|
||||||
// Decode the recipient NPUB to get the public key.
|
// Decode the recipient NPUB to get the public key.
|
||||||
log("Decoding recipient npub...");
|
log("Decoding recipient npub...");
|
||||||
const decoded = nip19.decode(recipientNpub);
|
const decoded = nip19.decode(recipientNpub);
|
||||||
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("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);
|
||||||
}
|
}
|
||||||
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>bitvid General Feedback Form</title>
|
<title>bitvid General Feedback Form</title>
|
||||||
<!-- Link to your main stylesheet -->
|
<!-- Link to your main stylesheet -->
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="../../css/style.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Override for form page to match modal field styling */
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
color: #e5e7eb; /* Tailwind's text-gray-200 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input, textarea, and select styling */
|
/* Input, textarea, and select styling */
|
||||||
@@ -51,8 +51,8 @@
|
|||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
background-color: #1f2937; /* Tailwind's bg-gray-800 */
|
||||||
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
color: #f3f4f6; /* Tailwind's text-gray-100 */
|
||||||
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.375rem; /* rounded-md */
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #3B82F6; /* blue-500 */
|
border-color: #3b82f6; /* blue-500 */
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 1px #3B82F6;
|
box-shadow: 0 0 0 1px #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkbox group styling */
|
/* Checkbox group styling */
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
/* Button styling */
|
/* Button styling */
|
||||||
button {
|
button {
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: #3B82F6; /* blue-500 */
|
background: #3b82f6; /* blue-500 */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -104,13 +104,13 @@
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: #F87171;
|
color: #f87171;
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
color: #3B82F6;
|
color: #3b82f6;
|
||||||
}
|
}
|
||||||
.warn {
|
.warn {
|
||||||
color: #FACC15;
|
color: #facc15;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Scrollbar styling for WebKit browsers */
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
@@ -122,13 +122,13 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #3B82F6;
|
background-color: #3b82f6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
/* Custom Scrollbar styling for Firefox */
|
/* Custom Scrollbar styling for Firefox */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3B82F6 transparent;
|
scrollbar-color: #3b82f6 transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Load nostr‑tools v2.10.4 -->
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
@@ -138,48 +138,112 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<p>
|
<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.
|
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>
|
</p>
|
||||||
<form id="feedback-form">
|
<form id="feedback-form">
|
||||||
<!-- Section 1: User Information -->
|
<!-- Section 1: User Information -->
|
||||||
<h2>1. User Information</h2>
|
<h2>1. User Information</h2>
|
||||||
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="userNpub"
|
||||||
|
placeholder="Enter your npub (optional)"
|
||||||
|
/>
|
||||||
<p>Are you a (check all that apply):</p>
|
<p>Are you a (check all that apply):</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
<label
|
||||||
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
><input type="checkbox" name="userRole" value="Viewer" />
|
||||||
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
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>
|
</div>
|
||||||
|
|
||||||
<!-- Section 2: General Feedback -->
|
<!-- Section 2: General Feedback -->
|
||||||
<h2>2. General Feedback</h2>
|
<h2>2. General Feedback</h2>
|
||||||
<label>How would you rate your experience on bitvid so far?</label>
|
<label>How would you rate your experience on bitvid so far?</label>
|
||||||
<div class="radio-group">
|
<div class="radio-group">
|
||||||
<label><input type="radio" name="experienceRating" value="Excellent" /> Excellent</label>
|
<label
|
||||||
<label><input type="radio" name="experienceRating" value="Good" /> Good</label>
|
><input type="radio" name="experienceRating" value="Excellent" />
|
||||||
<label><input type="radio" name="experienceRating" value="Average" /> Average</label>
|
Excellent</label
|
||||||
<label><input type="radio" name="experienceRating" value="Needs Improvement" /> Needs Improvement</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>
|
</div>
|
||||||
<label for="likeMost">What do you like most about bitvid?</label>
|
<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>
|
<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>
|
<label for="improvements">What would you like to see improved?</label>
|
||||||
<textarea id="improvements" rows="3" placeholder="Provide specific areas for improvement"></textarea>
|
<textarea
|
||||||
<label for="confusingFeatures">Are there any features or tools you find confusing or difficult to use?</label>
|
id="improvements"
|
||||||
<textarea id="confusingFeatures" rows="3" placeholder="Explain any challenges you’ve encountered"></textarea>
|
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 -->
|
<!-- Section 3: Additional Comments -->
|
||||||
<h2>3. Additional Comments</h2>
|
<h2>3. Additional Comments</h2>
|
||||||
<label for="otherSuggestions">Do you have any other suggestions or thoughts about bitvid?</label>
|
<label for="otherSuggestions"
|
||||||
<textarea id="otherSuggestions" rows="3" placeholder="Share any other feedback"></textarea>
|
>Do you have any other suggestions or thoughts about bitvid?</label
|
||||||
<label for="followUp">Would you like to be contacted for follow-up discussions?</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">
|
<select id="followUp">
|
||||||
<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="preferredContact">Preferred contact method (if applicable):</label>
|
<label for="preferredContact"
|
||||||
<input type="text" id="preferredContact" placeholder="Nostr DM, email, or other" />
|
>Preferred contact method (if applicable):</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="preferredContact"
|
||||||
|
placeholder="Nostr DM, email, or other"
|
||||||
|
/>
|
||||||
|
|
||||||
<button type="submit">Submit General Feedback</button>
|
<button type="submit">Submit General Feedback</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -206,51 +270,78 @@
|
|||||||
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
Relay,
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
// Set the recipient's NPUB (your personal NPUB)
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
const recipientNpub =
|
||||||
|
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
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",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
document.getElementById("feedback-form").addEventListener("submit", async (ev) => {
|
document
|
||||||
ev.preventDefault();
|
.getElementById("feedback-form")
|
||||||
clear();
|
.addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Section 1: User Information
|
// Section 1: User Information
|
||||||
const userNpub = document.getElementById("userNpub").value.trim();
|
const userNpub = document.getElementById("userNpub").value.trim();
|
||||||
const roleNodes = document.querySelectorAll('input[name="userRole"]:checked');
|
const roleNodes = document.querySelectorAll(
|
||||||
let userRoles = [];
|
'input[name="userRole"]:checked'
|
||||||
roleNodes.forEach(node => {
|
);
|
||||||
userRoles.push(node.value);
|
let userRoles = [];
|
||||||
});
|
roleNodes.forEach((node) => {
|
||||||
|
userRoles.push(node.value);
|
||||||
|
});
|
||||||
|
|
||||||
// Section 2: General Feedback
|
// Section 2: General Feedback
|
||||||
const experienceRadio = document.querySelector('input[name="experienceRating"]:checked');
|
const experienceRadio = document.querySelector(
|
||||||
const experienceRating = experienceRadio ? experienceRadio.value : "N/A";
|
'input[name="experienceRating"]:checked'
|
||||||
const likeMost = document.getElementById("likeMost").value.trim();
|
);
|
||||||
const improvements = document.getElementById("improvements").value.trim();
|
const experienceRating = experienceRadio
|
||||||
const confusingFeatures = document.getElementById("confusingFeatures").value.trim();
|
? 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
|
// Section 3: Additional Comments
|
||||||
const otherSuggestions = document.getElementById("otherSuggestions").value.trim();
|
const otherSuggestions = document
|
||||||
const followUp = document.getElementById("followUp").value.trim();
|
.getElementById("otherSuggestions")
|
||||||
const preferredContact = document.getElementById("preferredContact").value.trim();
|
.value.trim();
|
||||||
|
const followUp = document.getElementById("followUp").value.trim();
|
||||||
|
const preferredContact = document
|
||||||
|
.getElementById("preferredContact")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Construct the Markdown feedback content
|
// Construct the Markdown feedback content
|
||||||
const feedbackContent = `
|
const feedbackContent = `
|
||||||
# **bitvid General Feedback Form**
|
# **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.
|
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**
|
## **1. User Information**
|
||||||
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||||
- **Are you a (check all that apply):** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
- **Are you a (check all that apply):** ${
|
||||||
|
userRoles.length > 0 ? userRoles.join(", ") : "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
## **2. General Feedback**
|
## **2. General Feedback**
|
||||||
- **How would you rate your experience on bitvid so far?** ${experienceRating}
|
- **How would you rate your experience on bitvid so far?** ${experienceRating}
|
||||||
@@ -264,7 +355,9 @@ Your feedback helps us improve bitvid! Whether it’s a suggestion, a concern, o
|
|||||||
## **3. Additional Comments**
|
## **3. Additional Comments**
|
||||||
- **Do you have any other suggestions or thoughts about bitvid?**
|
- **Do you have any other suggestions or thoughts about bitvid?**
|
||||||
${otherSuggestions || "N/A"}
|
${otherSuggestions || "N/A"}
|
||||||
- **Would you like to be contacted for follow-up discussions?** ${followUp || "N/A"}
|
- **Would you like to be contacted for follow-up discussions?** ${
|
||||||
|
followUp || "N/A"
|
||||||
|
}
|
||||||
- **Preferred contact method (if applicable):** ${preferredContact || "N/A"}
|
- **Preferred contact method (if applicable):** ${preferredContact || "N/A"}
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -274,71 +367,87 @@ We review all feedback regularly to improve bitvid. While not all suggestions ma
|
|||||||
For additional discussions, reach out via bitvid’s Nostr support channels.
|
For additional discussions, reach out via bitvid’s Nostr support channels.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
log("[DEBUG] Constructed feedback content:\n" + feedbackContent);
|
log("[DEBUG] Constructed feedback content:\n" + feedbackContent);
|
||||||
|
|
||||||
// Decode the recipient NPUB to get the public key.
|
// Decode the recipient NPUB to get the public key.
|
||||||
log("Decoding recipient npub...");
|
log("Decoding recipient npub...");
|
||||||
const decoded = nip19.decode(recipientNpub);
|
const decoded = nip19.decode(recipientNpub);
|
||||||
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("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);
|
||||||
}
|
}
|
||||||
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>bitvid Feature Request Form</title>
|
<title>bitvid Feature Request Form</title>
|
||||||
<!-- Link to your main stylesheet -->
|
<!-- Link to your main stylesheet -->
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="../../css/style.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Override for form page to match modal field styling */
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #E5E7EB;
|
color: #e5e7eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input, textarea, and select styling */
|
/* Input, textarea, and select styling */
|
||||||
@@ -51,8 +51,8 @@
|
|||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0.75em;
|
margin-bottom: 0.75em;
|
||||||
background-color: #1F2937;
|
background-color: #1f2937;
|
||||||
color: #F3F4F6;
|
color: #f3f4f6;
|
||||||
border: 1px solid #374151;
|
border: 1px solid #374151;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #3B82F6;
|
border-color: #3b82f6;
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 1px #3B82F6;
|
box-shadow: 0 0 0 1px #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkbox group styling */
|
/* Checkbox group styling */
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
/* Button styling */
|
/* Button styling */
|
||||||
button {
|
button {
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: #3B82F6;
|
background: #3b82f6;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -97,13 +97,13 @@
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: #F87171;
|
color: #f87171;
|
||||||
}
|
}
|
||||||
.success {
|
.success {
|
||||||
color: #3B82F6;
|
color: #3b82f6;
|
||||||
}
|
}
|
||||||
.warn {
|
.warn {
|
||||||
color: #FACC15;
|
color: #facc15;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Scrollbar styling */
|
/* Custom Scrollbar styling */
|
||||||
@@ -115,12 +115,12 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #3B82F6;
|
background-color: #3b82f6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3B82F6 transparent;
|
scrollbar-color: #3b82f6 transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Load nostr‑tools v2.10.4 -->
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
@@ -130,38 +130,87 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<p>
|
<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.
|
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>
|
</p>
|
||||||
<form id="feature-form">
|
<form id="feature-form">
|
||||||
<!-- Section 1: User Information -->
|
<!-- Section 1: User Information -->
|
||||||
<h2>1. User Information</h2>
|
<h2>1. User Information</h2>
|
||||||
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="userNpub"
|
||||||
|
placeholder="Enter your npub (optional)"
|
||||||
|
/>
|
||||||
<p>Are you a (check all that apply):</p>
|
<p>Are you a (check all that apply):</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
<label
|
||||||
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
><input type="checkbox" name="userRole" value="Viewer" />
|
||||||
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
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>
|
</div>
|
||||||
|
|
||||||
<!-- Section 2: Feature Request Details -->
|
<!-- Section 2: Feature Request Details -->
|
||||||
<h2>2. Feature Request Details</h2>
|
<h2>2. Feature Request Details</h2>
|
||||||
<label for="featureName">Feature Name:</label>
|
<label for="featureName">Feature Name:</label>
|
||||||
<input type="text" id="featureName" placeholder="Short, descriptive title" required />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="featureName"
|
||||||
|
placeholder="Short, descriptive title"
|
||||||
|
required
|
||||||
|
/>
|
||||||
<label for="featureDescription">Describe the feature:</label>
|
<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>
|
<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>
|
<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>
|
<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>
|
<label for="beneficiary">Who would benefit from this feature?</label>
|
||||||
<input type="text" id="beneficiary" placeholder="e.g., Content Creators, Viewers, Both" />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="beneficiary"
|
||||||
|
placeholder="e.g., Content Creators, Viewers, Both"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Section 3: Additional Information -->
|
<!-- Section 3: Additional Information -->
|
||||||
<h2>3. Additional Information</h2>
|
<h2>3. Additional Information</h2>
|
||||||
<label for="existingPlatforms">Are there existing platforms that have this feature?</label>
|
<label for="existingPlatforms"
|
||||||
<textarea id="existingPlatforms" rows="2" placeholder="Provide examples if applicable"></textarea>
|
>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>
|
<label for="mockups">Do you have any mockups or examples?</label>
|
||||||
<textarea id="mockups" rows="2" placeholder="Links, screenshots, or diagrams"></textarea>
|
<textarea
|
||||||
<label for="willingToTest">Would you be willing to help test this feature if implemented?</label>
|
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">
|
<select id="willingToTest">
|
||||||
<option value="">Select an option</option>
|
<option value="">Select an option</option>
|
||||||
<option value="Yes">Yes</option>
|
<option value="Yes">Yes</option>
|
||||||
@@ -172,12 +221,27 @@
|
|||||||
<h2>4. Priority & Impact</h2>
|
<h2>4. Priority & Impact</h2>
|
||||||
<p>How urgent is this feature?</p>
|
<p>How urgent is this feature?</p>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label><input type="checkbox" name="featurePriority" value="High" /> High (Essential for platform success)</label>
|
<label
|
||||||
<label><input type="checkbox" name="featurePriority" value="Medium" /> Medium (Would improve experience significantly)</label>
|
><input type="checkbox" name="featurePriority" value="High" />
|
||||||
<label><input type="checkbox" name="featurePriority" value="Low" /> Low (Nice to have, but not urgent)</label>
|
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>
|
</div>
|
||||||
<label for="techChallenges">Does this feature require technical expertise to implement?</label>
|
<label for="techChallenges"
|
||||||
<textarea id="techChallenges" rows="2" placeholder="Describe any dependencies or challenges"></textarea>
|
>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>
|
<button type="submit">Submit Feature Request</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -204,50 +268,79 @@
|
|||||||
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
Relay,
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
// Set the recipient's NPUB (your personal NPUB)
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
const recipientNpub =
|
||||||
|
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
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",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
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
|
document
|
||||||
const featureName = document.getElementById("featureName").value.trim();
|
.getElementById("feature-form")
|
||||||
const featureDescription = document.getElementById("featureDescription").value.trim();
|
.addEventListener("submit", async (ev) => {
|
||||||
const featureImportance = document.getElementById("featureImportance").value.trim();
|
ev.preventDefault();
|
||||||
const beneficiary = document.getElementById("beneficiary").value.trim();
|
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 3: Additional Information
|
// Section 2: Feature Request Details
|
||||||
const existingPlatforms = document.getElementById("existingPlatforms").value.trim();
|
const featureName = document
|
||||||
const mockups = document.getElementById("mockups").value.trim();
|
.getElementById("featureName")
|
||||||
const willingToTest = document.getElementById("willingToTest").value.trim();
|
.value.trim();
|
||||||
|
const featureDescription = document
|
||||||
|
.getElementById("featureDescription")
|
||||||
|
.value.trim();
|
||||||
|
const featureImportance = document
|
||||||
|
.getElementById("featureImportance")
|
||||||
|
.value.trim();
|
||||||
|
const beneficiary = document
|
||||||
|
.getElementById("beneficiary")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Section 4: Priority & Impact
|
// Section 3: Additional Information
|
||||||
const priorityNodes = document.querySelectorAll('input[name="featurePriority"]:checked');
|
const existingPlatforms = document
|
||||||
let featurePriorities = [];
|
.getElementById("existingPlatforms")
|
||||||
priorityNodes.forEach(node => {
|
.value.trim();
|
||||||
featurePriorities.push(node.value);
|
const mockups = document.getElementById("mockups").value.trim();
|
||||||
});
|
const willingToTest = document
|
||||||
const techChallenges = document.getElementById("techChallenges").value.trim();
|
.getElementById("willingToTest")
|
||||||
|
.value.trim();
|
||||||
|
|
||||||
// Construct Markdown feature request
|
// Section 4: Priority & Impact
|
||||||
const featureRequestContent = `
|
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**
|
# **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.
|
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.
|
||||||
@@ -270,7 +363,9 @@ Have an idea for improving bitvid? We’d love to hear it! Please use this form
|
|||||||
${existingPlatforms || "N/A"}
|
${existingPlatforms || "N/A"}
|
||||||
- **Do you have any mockups or examples?**
|
- **Do you have any mockups or examples?**
|
||||||
${mockups || "N/A"}
|
${mockups || "N/A"}
|
||||||
- **Would you be willing to help test this feature if implemented?** ${willingToTest || "N/A"}
|
- **Would you be willing to help test this feature if implemented?** ${
|
||||||
|
willingToTest || "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
## **4. Priority & Impact**
|
## **4. Priority & Impact**
|
||||||
- **How urgent is this feature?**
|
- **How urgent is this feature?**
|
||||||
@@ -286,71 +381,90 @@ All feature requests are reviewed, but not all may be implemented. We prioritize
|
|||||||
For further discussions, reach out through bitvid’s Nostr support channels.
|
For further discussions, reach out through bitvid’s Nostr support channels.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
log("[DEBUG] Constructed feature request content:\n" + featureRequestContent);
|
log(
|
||||||
|
"[DEBUG] Constructed feature request content:\n" +
|
||||||
|
featureRequestContent
|
||||||
|
);
|
||||||
|
|
||||||
// Decode the recipient NPUB
|
// Decode the recipient NPUB
|
||||||
log("Decoding recipient npub...");
|
log("Decoding recipient npub...");
|
||||||
const decoded = nip19.decode(recipientNpub);
|
const decoded = nip19.decode(recipientNpub);
|
||||||
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("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);
|
||||||
}
|
}
|
||||||
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
257
src/sw.min.js
vendored
257
src/sw.min.js
vendored
@@ -1,132 +1,143 @@
|
|||||||
(() => {
|
(() => {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let cancelled = false;
|
|
||||||
|
|
||||||
// Handle skip waiting message
|
let cancelled = false;
|
||||||
self.addEventListener('message', event => {
|
|
||||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
// Handle skip waiting message
|
||||||
self.skipWaiting()
|
self.addEventListener("message", (event) => {
|
||||||
|
if (event.data && event.data.type === "SKIP_WAITING") {
|
||||||
|
self.skipWaiting();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Immediately install and activate
|
||||||
|
self.addEventListener("install", () => {
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Claim clients on activation
|
||||||
|
self.addEventListener("activate", (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
Promise.all([
|
||||||
|
clients.claim(),
|
||||||
|
self.skipWaiting(),
|
||||||
|
caches
|
||||||
|
.keys()
|
||||||
|
.then((cacheNames) =>
|
||||||
|
Promise.all(cacheNames.map((cacheName) => caches.delete(cacheName)))
|
||||||
|
),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle fetch events
|
||||||
|
self.addEventListener("fetch", (s) => {
|
||||||
|
const t = ((s) => {
|
||||||
|
const { url: t } = s.request;
|
||||||
|
|
||||||
|
// Only handle webtorrent requests
|
||||||
|
if (!t.includes(self.registration.scope + "webtorrent/")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle keepalive requests
|
||||||
|
if (t.includes(self.registration.scope + "webtorrent/keepalive/")) {
|
||||||
|
return new Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle cancel requests
|
||||||
|
if (t.includes(self.registration.scope + "webtorrent/cancel/")) {
|
||||||
|
return new Response(
|
||||||
|
new ReadableStream({
|
||||||
|
cancel() {
|
||||||
|
cancelled = true;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle streaming requests
|
||||||
|
return (async function ({ request: s }) {
|
||||||
|
const { url: t, method: n, headers: o, destination: a } = s;
|
||||||
|
|
||||||
|
// Get all window clients
|
||||||
|
const l = await clients.matchAll({
|
||||||
|
type: "window",
|
||||||
|
includeUncontrolled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create message channel and wait for response
|
||||||
|
const [r, i] = await new Promise((e) => {
|
||||||
|
for (const s of l) {
|
||||||
|
const l = new MessageChannel(),
|
||||||
|
{ port1: r, port2: i } = l;
|
||||||
|
r.onmessage = ({ data: s }) => {
|
||||||
|
e([s, r]);
|
||||||
|
};
|
||||||
|
s.postMessage(
|
||||||
|
{
|
||||||
|
url: t,
|
||||||
|
method: n,
|
||||||
|
headers: Object.fromEntries(o.entries()),
|
||||||
|
scope: self.registration.scope,
|
||||||
|
destination: a,
|
||||||
|
type: "webtorrent",
|
||||||
|
},
|
||||||
|
[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let c = null;
|
||||||
|
|
||||||
|
const d = () => {
|
||||||
|
i.postMessage(false);
|
||||||
|
clearTimeout(c);
|
||||||
|
i.onmessage = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle non-streaming response
|
||||||
|
if (r.body !== "STREAM") {
|
||||||
|
d();
|
||||||
|
return new Response(r.body, r);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// Immediately install and activate
|
// Handle streaming response
|
||||||
self.addEventListener("install", () => {
|
return new Response(
|
||||||
self.skipWaiting()
|
new ReadableStream({
|
||||||
})
|
pull: (s) =>
|
||||||
|
new Promise((t) => {
|
||||||
// Claim clients on activation
|
i.onmessage = ({ data: e }) => {
|
||||||
self.addEventListener('activate', event => {
|
if (e) {
|
||||||
event.waitUntil(
|
s.enqueue(e);
|
||||||
Promise.all([
|
} else {
|
||||||
clients.claim(),
|
d();
|
||||||
self.skipWaiting(),
|
s.close();
|
||||||
caches.keys().then(cacheNames =>
|
}
|
||||||
Promise.all(cacheNames.map(cacheName => caches.delete(cacheName)))
|
t();
|
||||||
)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle fetch events
|
|
||||||
self.addEventListener("fetch", s => {
|
|
||||||
const t = (s => {
|
|
||||||
const { url: t } = s.request;
|
|
||||||
|
|
||||||
// Only handle webtorrent requests
|
|
||||||
if (!t.includes(self.registration.scope + "webtorrent/")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle keepalive requests
|
|
||||||
if (t.includes(self.registration.scope + "webtorrent/keepalive/")) {
|
|
||||||
return new Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle cancel requests
|
|
||||||
if (t.includes(self.registration.scope + "webtorrent/cancel/")) {
|
|
||||||
return new Response(new ReadableStream({
|
|
||||||
cancel() {
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle streaming requests
|
|
||||||
return async function({ request: s }) {
|
|
||||||
const { url: t, method: n, headers: o, destination: a } = s;
|
|
||||||
|
|
||||||
// Get all window clients
|
|
||||||
const l = await clients.matchAll({
|
|
||||||
type: "window",
|
|
||||||
includeUncontrolled: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create message channel and wait for response
|
|
||||||
const [r, i] = await new Promise(e => {
|
|
||||||
for (const s of l) {
|
|
||||||
const l = new MessageChannel,
|
|
||||||
{ port1: r, port2: i } = l;
|
|
||||||
r.onmessage = ({ data: s }) => {
|
|
||||||
e([s, r])
|
|
||||||
};
|
|
||||||
s.postMessage({
|
|
||||||
url: t,
|
|
||||||
method: n,
|
|
||||||
headers: Object.fromEntries(o.entries()),
|
|
||||||
scope: self.registration.scope,
|
|
||||||
destination: a,
|
|
||||||
type: "webtorrent"
|
|
||||||
}, [i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let c = null;
|
|
||||||
|
|
||||||
const d = () => {
|
|
||||||
i.postMessage(false);
|
|
||||||
clearTimeout(c);
|
|
||||||
i.onmessage = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle non-streaming response
|
if (!cancelled && a !== "document") {
|
||||||
if (r.body !== "STREAM") {
|
clearTimeout(c);
|
||||||
|
c = setTimeout(() => {
|
||||||
d();
|
d();
|
||||||
return new Response(r.body, r);
|
t();
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle streaming response
|
i.postMessage(true);
|
||||||
return new Response(new ReadableStream({
|
}),
|
||||||
pull: s => new Promise(t => {
|
cancel() {
|
||||||
i.onmessage = ({ data: e }) => {
|
d();
|
||||||
if (e) {
|
},
|
||||||
s.enqueue(e);
|
}),
|
||||||
} else {
|
r
|
||||||
d();
|
);
|
||||||
s.close();
|
})(s);
|
||||||
}
|
})(s);
|
||||||
t();
|
|
||||||
};
|
if (t) {
|
||||||
|
s.respondWith(t);
|
||||||
if (!cancelled && a !== "document") {
|
}
|
||||||
clearTimeout(c);
|
});
|
||||||
c = setTimeout(() => {
|
})();
|
||||||
d();
|
|
||||||
t();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
i.postMessage(true);
|
|
||||||
}),
|
|
||||||
cancel() {
|
|
||||||
d();
|
|
||||||
}
|
|
||||||
}), r);
|
|
||||||
}(s);
|
|
||||||
})(s);
|
|
||||||
|
|
||||||
if (t) {
|
|
||||||
s.respondWith(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
Reference in New Issue
Block a user