mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2025-09-08 15:08:44 +00:00
Merge pull request #5 from PR0M3TH3AN/unstable
Added custom Nostr DM form submissions for: Whitelisting, Bug Reporting, Feature Requesting and Content Appeals.
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
<!-- components/application-form.html
|
||||||
|
https://bitvid.network?modal=application => open nostrFormModal
|
||||||
|
-->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -8,23 +11,13 @@
|
|||||||
<link href="css/tailwind.min.css" rel="stylesheet" />
|
<link href="css/tailwind.min.css" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-900 text-gray-100">
|
<body class="bg-gray-900 text-gray-100">
|
||||||
<!-- Button to open the modal -->
|
<!-- Application Form Modal -->
|
||||||
<div class="p-4">
|
|
||||||
<button
|
|
||||||
id="openNostrForm"
|
|
||||||
class="bg-blue-500 px-4 py-2 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
Open Nostr Form
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal Container -->
|
|
||||||
<div
|
<div
|
||||||
id="nostrFormModal"
|
id="nostrFormModal"
|
||||||
class="fixed inset-0 z-50 hidden"
|
class="fixed inset-0 z-50 hidden"
|
||||||
style="background: transparent"
|
style="background: transparent"
|
||||||
>
|
>
|
||||||
<!-- Overlay Layer with dark background and blur -->
|
<!-- Dark/blur overlay -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 z-10"
|
class="absolute inset-0 z-10"
|
||||||
style="
|
style="
|
||||||
@@ -34,18 +27,21 @@
|
|||||||
"
|
"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Modal Container -->
|
<!-- Outer container with full width and centered content -->
|
||||||
<div
|
<div
|
||||||
class="relative modal-container h-full w-full flex items-center justify-center overflow-y-auto z-20"
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
>
|
>
|
||||||
|
<!-- Inner content container -->
|
||||||
<div
|
<div
|
||||||
class="modal-content bg-gray-900 w-full max-w-lg md:max-w-2xl my-0 rounded-lg overflow-hidden relative max-h-[90vh]"
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
>
|
>
|
||||||
<!-- Top Bar -->
|
<!-- Header bar with updated styling -->
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent transition-transform duration-300 p-4 flex items-center justify-between"
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between border-b border-gray-700"
|
||||||
>
|
>
|
||||||
<h2 class="text-xl font-bold text-white">My Nostr Form</h2>
|
<h2 class="text-3xl font-extrabold text-white tracking-wide pb-2">
|
||||||
|
Application Form
|
||||||
|
</h2>
|
||||||
<button
|
<button
|
||||||
id="closeNostrFormModal"
|
id="closeNostrFormModal"
|
||||||
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
@@ -67,19 +63,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal Content -->
|
<!-- Main Content -->
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="flex justify-center">
|
<div class="w-full" style="height: 80vh">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://formstr.app/#/f/naddr1qvzqqqr4mqpzpcf63zey4c02lz9x4zkn2ny5kmz3g8v8fj9hvehyps5tuj7u272gqythwumn8ghj7un9d3shjtnwdaehgu3wvfskuep0qqsky6t5we5kgh6hdp5hgetvd9ehgh6pwpcxc6trv96xjmmwtarx7und0qc765?hideTitleImage=true"
|
src="https://beta.bitvid.network/components/iframe_forms/iframe-application-form.html"
|
||||||
height="700px"
|
class="w-full h-full"
|
||||||
width="480px"
|
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
style="
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
border-style: none;
|
|
||||||
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
"
|
|
||||||
cellspacing="0"
|
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,19 +78,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Script to toggle modal -->
|
<!-- Optional Script for Opening/Closing the Modal -->
|
||||||
<script>
|
<script>
|
||||||
const openBtn = document.getElementById("openNostrForm");
|
const openBtn = document.getElementById("openNostrForm");
|
||||||
const modal = document.getElementById("nostrFormModal");
|
const appModal = document.getElementById("nostrFormModal");
|
||||||
const closeBtn = document.getElementById("closeNostrFormModal");
|
const closeBtn = document.getElementById("closeNostrFormModal");
|
||||||
|
|
||||||
openBtn.addEventListener("click", () => {
|
// Open the modal when the designated button is clicked.
|
||||||
modal.classList.remove("hidden");
|
if (openBtn) {
|
||||||
});
|
openBtn.addEventListener("click", () => {
|
||||||
|
appModal.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
closeBtn.addEventListener("click", () => {
|
// Close the modal when the close button is clicked.
|
||||||
modal.classList.add("hidden");
|
if (closeBtn) {
|
||||||
});
|
closeBtn.addEventListener("click", () => {
|
||||||
|
appModal.classList.add("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
68
src/components/bug-fix-form.html
Normal file
68
src/components/bug-fix-form.html
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<!-- components/bug-fix-form.html
|
||||||
|
// https://bitvid.network?modal=bug => open bugFixModal
|
||||||
|
// Admin: npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe
|
||||||
|
// Form:
|
||||||
|
// Responses:
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
id="bugFixModal"
|
||||||
|
class="fixed inset-0 z-50 hidden"
|
||||||
|
style="background: transparent"
|
||||||
|
>
|
||||||
|
<!-- Dark/blur overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 z-10"
|
||||||
|
style="
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
>
|
||||||
|
<!-- Header bar (sticky) with updated title styling -->
|
||||||
|
<div
|
||||||
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between border-b border-gray-700"
|
||||||
|
>
|
||||||
|
<h2 class="text-3xl font-extrabold text-white tracking-wide pb-2">
|
||||||
|
Bug Fix Form
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
id="closeBugFixModal"
|
||||||
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6 text-gray-300"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full" style="height: 80vh">
|
||||||
|
<iframe
|
||||||
|
src="https://beta.bitvid.network/components/iframe_forms/iframe-bug-fix-form.html"
|
||||||
|
class="w-full h-full"
|
||||||
|
frameborder="0"
|
||||||
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
65
src/components/content-appeals-form.html
Normal file
65
src/components/content-appeals-form.html
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<!-- components/content-appeals-form.html
|
||||||
|
https://bitvid.network?modal=appeals => open contentAppealsModal
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
id="contentAppealsModal"
|
||||||
|
class="fixed inset-0 z-50 hidden"
|
||||||
|
style="background: transparent"
|
||||||
|
>
|
||||||
|
<!-- Dark/blur overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 z-10"
|
||||||
|
style="
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
>
|
||||||
|
<!-- Header bar (sticky) with improved title styling -->
|
||||||
|
<div
|
||||||
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between border-b border-gray-700"
|
||||||
|
>
|
||||||
|
<h2 class="text-3xl font-extrabold text-white tracking-wide pb-2">
|
||||||
|
Content Appeals Form
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
id="closeContentAppealsModal"
|
||||||
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6 text-gray-300"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full" style="height: 80vh">
|
||||||
|
<iframe
|
||||||
|
src="https://beta.bitvid.network/components/iframe_forms/iframe-content-appeals-form.html"
|
||||||
|
class="w-full h-full"
|
||||||
|
frameborder="0"
|
||||||
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
65
src/components/feature-request-form.html
Normal file
65
src/components/feature-request-form.html
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<!-- components/feature-request-form.html
|
||||||
|
// https://bitvid.network?modal=feature => open featureRequestModal
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
id="featureRequestModal"
|
||||||
|
class="fixed inset-0 z-50 hidden"
|
||||||
|
style="background: transparent"
|
||||||
|
>
|
||||||
|
<!-- Dark/blur overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 z-10"
|
||||||
|
style="
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
>
|
||||||
|
<!-- Header bar (sticky) with updated title styling -->
|
||||||
|
<div
|
||||||
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between border-b border-gray-700"
|
||||||
|
>
|
||||||
|
<h2 class="text-3xl font-extrabold text-white tracking-wide pb-2">
|
||||||
|
Feature Request Form
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
id="closeFeatureRequestModal"
|
||||||
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6 text-gray-300"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full" style="height: 80vh">
|
||||||
|
<iframe
|
||||||
|
src="https://beta.bitvid.network/components/iframe_forms/iframe-request-form.html"
|
||||||
|
class="w-full h-full"
|
||||||
|
frameborder="0"
|
||||||
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
69
src/components/general-feedback-form.html
Normal file
69
src/components/general-feedback-form.html
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<!-- components/content-appeals-form.html
|
||||||
|
// https://bitvid.network?modal=feedback => open generalFeedbackModal
|
||||||
|
//
|
||||||
|
// Admin: npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe
|
||||||
|
// Form:
|
||||||
|
// Responses:
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
id="generalFeedbackModal"
|
||||||
|
class="fixed inset-0 z-50 hidden"
|
||||||
|
style="background: transparent"
|
||||||
|
>
|
||||||
|
<!-- Dark/blur overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 z-10"
|
||||||
|
style="
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
>
|
||||||
|
<!-- Header bar (sticky) with updated title styling -->
|
||||||
|
<div
|
||||||
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between border-b border-gray-700"
|
||||||
|
>
|
||||||
|
<h2 class="text-3xl font-extrabold text-white tracking-wide pb-2">
|
||||||
|
General Feedback Form
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
id="closeGeneralFeedbackModal"
|
||||||
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6 text-gray-300"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full" style="height: 80vh">
|
||||||
|
<iframe
|
||||||
|
src="https://beta.bitvid.network/components/iframe_forms/iframe-feedback-form.html"
|
||||||
|
class="w-full h-full"
|
||||||
|
frameborder="0"
|
||||||
|
style="border: none; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2)"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
403
src/components/iframe_forms/iframe-application-form.html
Normal file
403
src/components/iframe_forms/iframe-application-form.html
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>bitvid Whitelist Application Form</title>
|
||||||
|
<!-- Link to your main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<style>
|
||||||
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
|
/* Remove width constraints from body so our container can be full width */
|
||||||
|
body {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override the .container to use the full available width */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card-like container for the form */
|
||||||
|
.form-container {
|
||||||
|
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels in a light gray */
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input, textarea, select mimic modal field styles */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
||||||
|
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
||||||
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #3B82F6; /* blue-500 */
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 1px #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for checkboxes – display inline with a label */
|
||||||
|
.checkbox-group label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styled similarly to modal publish button */
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3B82F6; /* blue-500 */
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status log area */
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111827;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #F87171; /* a red tint */
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #3B82F6; /* blue-500 */
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #FACC15; /* a yellow tone */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
/* Custom Scrollbar styling for Firefox */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3B82F6 transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<p>
|
||||||
|
bitvid is currently in early access. If you would like to request access, please fill out this application. Applications will be reviewed manually, and approvals will be based on alignment with bitvid’s community values.
|
||||||
|
</p>
|
||||||
|
<form id="wl-form">
|
||||||
|
<!-- 1. Applicant Information -->
|
||||||
|
<h2>1. Applicant Information</h2>
|
||||||
|
<label for="applicantNpub">Nostr Public Key (npub):</label>
|
||||||
|
<input type="text" id="applicantNpub" placeholder="Enter your npub" required />
|
||||||
|
|
||||||
|
<label for="contactMethod">Preferred Contact Method (if applicable):</label>
|
||||||
|
<input type="text" id="contactMethod" placeholder="Nostr DM, email, or other" />
|
||||||
|
|
||||||
|
<label for="username">Username or Alias (if applicable):</label>
|
||||||
|
<input type="text" id="username" placeholder="Enter your preferred name" />
|
||||||
|
|
||||||
|
<!-- 2. Content Intent -->
|
||||||
|
<h2>2. Content Intent</h2>
|
||||||
|
<p>What type of content do you plan to upload? (Check all that apply)</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="contentType" value="Educational" /> Educational</label>
|
||||||
|
<label><input type="checkbox" name="contentType" value="Entertainment" /> Entertainment</label>
|
||||||
|
<label><input type="checkbox" name="contentType" value="News & Journalism" /> News & Journalism</label>
|
||||||
|
<label><input type="checkbox" name="contentType" value="Creative Works" /> Creative Works</label>
|
||||||
|
<label><input type="checkbox" name="contentType" value="Discussions & Opinions" /> Discussions & Opinions</label>
|
||||||
|
<label><input type="checkbox" name="contentType" value="Other" /> Other</label>
|
||||||
|
</div>
|
||||||
|
<label for="otherContent">Other (please specify):</label>
|
||||||
|
<input type="text" id="otherContent" placeholder="Describe if Other was selected" />
|
||||||
|
|
||||||
|
<label for="whyJoin">Why do you want to join bitvid?</label>
|
||||||
|
<textarea id="whyJoin" rows="3" placeholder="Explain your motivation"></textarea>
|
||||||
|
|
||||||
|
<label for="priorExperience">Have you created content on other platforms before?</label>
|
||||||
|
<select id="priorExperience">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="experienceLinks">If yes, provide links or references to your previous work:</label>
|
||||||
|
<textarea id="experienceLinks" rows="2" placeholder="Paste links or describe your work"></textarea>
|
||||||
|
|
||||||
|
<!-- 3. Community Engagement -->
|
||||||
|
<h2>3. Community Engagement</h2>
|
||||||
|
<label for="familiarGuidelines">Are you familiar with bitvid’s Community Guidelines?</label>
|
||||||
|
<select id="familiarGuidelines">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="agreeGuidelines">Do you agree to follow the guidelines and respect decentralized moderation?</label>
|
||||||
|
<select id="agreeGuidelines">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="communityContribution">How do you plan to contribute positively to the bitvid community?</label>
|
||||||
|
<textarea id="communityContribution" rows="3" placeholder="Explain your approach"></textarea>
|
||||||
|
|
||||||
|
<!-- 4. Additional Information -->
|
||||||
|
<h2>4. Additional Information</h2>
|
||||||
|
<label for="specialSkills">Do you have any special skills or interests that could help improve bitvid?</label>
|
||||||
|
<textarea id="specialSkills" rows="3" placeholder="e.g., software development, moderation, design, advocacy"></textarea>
|
||||||
|
|
||||||
|
<label for="testFeatures">Would you be interested in helping test new features?</label>
|
||||||
|
<select id="testFeatures">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 5. Declaration -->
|
||||||
|
<h2>5. Declaration</h2>
|
||||||
|
<p>
|
||||||
|
By submitting this application, you confirm that:
|
||||||
|
<br />- The information provided is accurate.
|
||||||
|
<br />- You understand that approval is based on community alignment and available capacity.
|
||||||
|
<br />- You acknowledge that whitelist status may be revoked if guidelines are violated.
|
||||||
|
</p>
|
||||||
|
<label for="signature">Signature (Digital or Written):</label>
|
||||||
|
<input type="text" id="signature" placeholder="Your signature" />
|
||||||
|
|
||||||
|
<label for="declarationDate">Date:</label>
|
||||||
|
<input type="date" id="declarationDate" />
|
||||||
|
|
||||||
|
<button type="submit">Submit Application</button>
|
||||||
|
</form>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging functions for both on-page and console output.
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
||||||
|
|
||||||
|
// Set the recipient's NPUB (your personal NPUB) here.
|
||||||
|
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net"
|
||||||
|
];
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
document.getElementById("wl-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
try {
|
||||||
|
// Retrieve applicant-provided input.
|
||||||
|
const applicantNpub = document.getElementById("applicantNpub").value.trim();
|
||||||
|
const contactMethod = document.getElementById("contactMethod").value.trim();
|
||||||
|
const username = document.getElementById("username").value.trim();
|
||||||
|
|
||||||
|
// Section 2: Content Intent
|
||||||
|
// Get all checked content types
|
||||||
|
const contentTypeNodes = document.querySelectorAll('input[name="contentType"]:checked');
|
||||||
|
let contentTypes = [];
|
||||||
|
contentTypeNodes.forEach((node) => {
|
||||||
|
contentTypes.push(node.value);
|
||||||
|
});
|
||||||
|
const otherContent = document.getElementById("otherContent").value.trim();
|
||||||
|
const whyJoin = document.getElementById("whyJoin").value.trim();
|
||||||
|
const priorExperience = document.getElementById("priorExperience").value.trim();
|
||||||
|
const experienceLinks = document.getElementById("experienceLinks").value.trim();
|
||||||
|
|
||||||
|
// Section 3: Community Engagement
|
||||||
|
const familiarGuidelines = document.getElementById("familiarGuidelines").value.trim();
|
||||||
|
const agreeGuidelines = document.getElementById("agreeGuidelines").value.trim();
|
||||||
|
const communityContribution = document.getElementById("communityContribution").value.trim();
|
||||||
|
|
||||||
|
// Section 4: Additional Information
|
||||||
|
const specialSkills = document.getElementById("specialSkills").value.trim();
|
||||||
|
const testFeatures = document.getElementById("testFeatures").value.trim();
|
||||||
|
|
||||||
|
// Section 5: Declaration
|
||||||
|
const signature = document.getElementById("signature").value.trim();
|
||||||
|
const declarationDate = document.getElementById("declarationDate").value.trim();
|
||||||
|
|
||||||
|
// Construct the whitelist application content.
|
||||||
|
const applicationContent = `
|
||||||
|
# **bitvid Whitelist Application Form**
|
||||||
|
|
||||||
|
**1. Applicant Information**
|
||||||
|
- **Nostr Public Key (npub):** ${applicantNpub}
|
||||||
|
- **Preferred Contact Method:** ${contactMethod || "N/A"}
|
||||||
|
- **Username or Alias:** ${username || "N/A"}
|
||||||
|
|
||||||
|
**2. Content Intent**
|
||||||
|
- **Planned Content Types:** ${contentTypes.join(", ") || "N/A"}
|
||||||
|
- **Other (if applicable):** ${otherContent || "N/A"}
|
||||||
|
- **Why do you want to join bitvid?**
|
||||||
|
${whyJoin || "N/A"}
|
||||||
|
- **Have you created content on other platforms before?** ${priorExperience || "N/A"}
|
||||||
|
- **Links or references to your previous work:**
|
||||||
|
${experienceLinks || "N/A"}
|
||||||
|
|
||||||
|
**3. Community Engagement**
|
||||||
|
- **Familiar with bitvid’s Community Guidelines?** ${familiarGuidelines || "N/A"}
|
||||||
|
- **Agree to follow guidelines and respect moderation?** ${agreeGuidelines || "N/A"}
|
||||||
|
- **How do you plan to contribute to the community?**
|
||||||
|
${communityContribution || "N/A"}
|
||||||
|
|
||||||
|
**4. Additional Information**
|
||||||
|
- **Special skills or interests:**
|
||||||
|
${specialSkills || "N/A"}
|
||||||
|
- **Interested in testing new features?** ${testFeatures || "N/A"}
|
||||||
|
|
||||||
|
**5. Declaration**
|
||||||
|
By submitting this application, you confirm that:
|
||||||
|
- The information provided is accurate.
|
||||||
|
- You understand that approval is based on community alignment and available capacity.
|
||||||
|
- You acknowledge that whitelist status may be revoked if guidelines are violated.
|
||||||
|
|
||||||
|
**Signature (Digital or Written):** ${signature || "N/A"}
|
||||||
|
**Date:** ${declarationDate || "N/A"}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Processing Time:** Applications will be reviewed within **7-14 days**. If approved, you will receive a confirmation via your provided contact method. If additional information is needed, we will reach out.
|
||||||
|
|
||||||
|
For further questions, contact us through bitvid’s Nostr support channels.
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
log("[DEBUG] Constructed application content:\n" + applicationContent);
|
||||||
|
|
||||||
|
// Decode the recipient NPUB to get the public key.
|
||||||
|
log("Decoding recipient npub...");
|
||||||
|
const decoded = nip19.decode(recipientNpub);
|
||||||
|
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Generate an ephemeral key pair.
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey();
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv);
|
||||||
|
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Encrypt the application content.
|
||||||
|
log("Encrypting application content (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, applicationContent);
|
||||||
|
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// Build the Nostr event template.
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const eventTemplate = {
|
||||||
|
kind: 4,
|
||||||
|
created_at: now,
|
||||||
|
tags: [["p", targetPubHex]],
|
||||||
|
content: ciphertext,
|
||||||
|
};
|
||||||
|
log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
|
||||||
|
|
||||||
|
// Finalize the event (computing the id and signature).
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||||
|
|
||||||
|
// Publish the event to all relays.
|
||||||
|
log("Publishing the application to relays...");
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// For each relay, subscribe to verify the event appears.
|
||||||
|
for (const url of RELAYS) {
|
||||||
|
log("Connecting to " + url + " for subscription...");
|
||||||
|
const relay = await Relay.connect(url);
|
||||||
|
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
|
||||||
|
onEvent(foundEvent) {
|
||||||
|
if (foundEvent.id === event.id) {
|
||||||
|
log("[" + url + "] => Found our application in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Done. If the logs show that at least one relay accepted the event and the application was found in storage, a moderator will review your application within 7-14 days.");
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
399
src/components/iframe_forms/iframe-bug-fix-form.html
Normal file
399
src/components/iframe_forms/iframe-bug-fix-form.html
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>bitvid Bug Report Form</title>
|
||||||
|
<!-- Link to your main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<style>
|
||||||
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
|
/* Remove width constraints from body so our container can be full width */
|
||||||
|
body {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override the .container to use the full available width */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card-like container for the form, similar to modal-content */
|
||||||
|
.form-container {
|
||||||
|
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels in a light gray */
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input, textarea, and select mimic modal field styles */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
||||||
|
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
||||||
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #3B82F6; /* blue-500 */
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 1px #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox group styling */
|
||||||
|
.checkbox-group label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styled similarly to modal publish button */
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3B82F6; /* blue-500 */
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status log area */
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111827;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #F87171; /* a red tint */
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #3B82F6; /* blue-500 */
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #FACC15; /* a yellow tone */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
/* Custom Scrollbar styling for Firefox */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3B82F6 transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<p>
|
||||||
|
If you have encountered a bug or technical issue on bitvid, please fill out this form to help us diagnose and resolve the problem. Providing detailed information will assist us in troubleshooting more efficiently.
|
||||||
|
</p>
|
||||||
|
<form id="bug-form">
|
||||||
|
<!-- Section 1: User Information -->
|
||||||
|
<h2>1. User Information</h2>
|
||||||
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
|
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
||||||
|
<p>Are you a (check all that apply):</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 2: Bug Details -->
|
||||||
|
<h2>2. Bug Details</h2>
|
||||||
|
<label for="issueDescription">Describe the issue:</label>
|
||||||
|
<textarea id="issueDescription" rows="3" placeholder="Provide a clear and concise description of the problem"></textarea>
|
||||||
|
<label for="stepsToReproduce">Steps to reproduce the bug:</label>
|
||||||
|
<textarea id="stepsToReproduce" rows="3" placeholder="List the actions taken before encountering the issue"></textarea>
|
||||||
|
<label for="expectedBehavior">Expected behavior:</label>
|
||||||
|
<textarea id="expectedBehavior" rows="2" placeholder="What should have happened instead?"></textarea>
|
||||||
|
<label for="actualBehavior">Actual behavior:</label>
|
||||||
|
<textarea id="actualBehavior" rows="2" placeholder="What actually happened?"></textarea>
|
||||||
|
|
||||||
|
<!-- Section 3: Device & Environment -->
|
||||||
|
<h2>3. Device & Environment</h2>
|
||||||
|
<p>What device were you using? (check all that apply):</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="deviceUsed" value="Desktop" /> Desktop</label>
|
||||||
|
<label><input type="checkbox" name="deviceUsed" value="Mobile" /> Mobile</label>
|
||||||
|
<label><input type="checkbox" name="deviceUsed" value="Tablet" /> Tablet</label>
|
||||||
|
</div>
|
||||||
|
<label for="operatingSystem">Operating System:</label>
|
||||||
|
<input type="text" id="operatingSystem" placeholder="e.g., Windows, macOS, Linux, iOS, Android" />
|
||||||
|
<label for="browserInfo">Browser (if applicable):</label>
|
||||||
|
<input type="text" id="browserInfo" placeholder="e.g., Chrome, Firefox, Safari" />
|
||||||
|
<label for="usingVPN">Are you using a VPN or privacy-focused browser settings?</label>
|
||||||
|
<select id="usingVPN">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Section 4: Screenshots or Logs -->
|
||||||
|
<h2>4. Screenshots or Logs (If Available)</h2>
|
||||||
|
<label for="screenshotInfo">Can you provide a screenshot or screen recording?</label>
|
||||||
|
<textarea id="screenshotInfo" rows="2" placeholder="Attach if possible (e.g., URL to screenshot)"></textarea>
|
||||||
|
<label for="errorMessages">Did the issue generate any error messages?</label>
|
||||||
|
<textarea id="errorMessages" rows="2" placeholder="Include the exact error messages, if any"></textarea>
|
||||||
|
<label for="consoleLogs">Any relevant console logs?</label>
|
||||||
|
<textarea id="consoleLogs" rows="2" placeholder="Paste any browser console logs if available"></textarea>
|
||||||
|
|
||||||
|
<!-- Section 5: Additional Information -->
|
||||||
|
<h2>5. Additional Information</h2>
|
||||||
|
<label for="bugFrequency">How frequently does this bug occur?</label>
|
||||||
|
<select id="bugFrequency">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Every time">Every time</option>
|
||||||
|
<option value="Occasionally">Occasionally</option>
|
||||||
|
<option value="Rarely">Rarely</option>
|
||||||
|
</select>
|
||||||
|
<label for="impactCore">Does this bug impact core functionality (e.g., video playback, uploads, etc.)?</label>
|
||||||
|
<select id="impactCore">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
<label for="additionalNotes">Any additional notes or suggestions?</label>
|
||||||
|
<textarea id="additionalNotes" rows="3" placeholder="Describe any other relevant details"></textarea>
|
||||||
|
|
||||||
|
<button type="submit">Submit Bug Report</button>
|
||||||
|
</form>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging functions for both on-page and console output.
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
Relay
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
|
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net"
|
||||||
|
];
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
document.getElementById("bug-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
try {
|
||||||
|
// Section 1: User Information
|
||||||
|
const userNpub = document.getElementById("userNpub").value.trim();
|
||||||
|
const roleNodes = document.querySelectorAll('input[name="userRole"]:checked');
|
||||||
|
let userRoles = [];
|
||||||
|
roleNodes.forEach(node => {
|
||||||
|
userRoles.push(node.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Section 2: Bug Details
|
||||||
|
const issueDescription = document.getElementById("issueDescription").value.trim();
|
||||||
|
const stepsToReproduce = document.getElementById("stepsToReproduce").value.trim();
|
||||||
|
const expectedBehavior = document.getElementById("expectedBehavior").value.trim();
|
||||||
|
const actualBehavior = document.getElementById("actualBehavior").value.trim();
|
||||||
|
|
||||||
|
// Section 3: Device & Environment
|
||||||
|
const deviceNodes = document.querySelectorAll('input[name="deviceUsed"]:checked');
|
||||||
|
let devicesUsed = [];
|
||||||
|
deviceNodes.forEach(node => {
|
||||||
|
devicesUsed.push(node.value);
|
||||||
|
});
|
||||||
|
const operatingSystem = document.getElementById("operatingSystem").value.trim();
|
||||||
|
const browserInfo = document.getElementById("browserInfo").value.trim();
|
||||||
|
const usingVPN = document.getElementById("usingVPN").value.trim();
|
||||||
|
|
||||||
|
// Section 4: Screenshots or Logs
|
||||||
|
const screenshotInfo = document.getElementById("screenshotInfo").value.trim();
|
||||||
|
const errorMessages = document.getElementById("errorMessages").value.trim();
|
||||||
|
const consoleLogs = document.getElementById("consoleLogs").value.trim();
|
||||||
|
|
||||||
|
// Section 5: Additional Information
|
||||||
|
const bugFrequency = document.getElementById("bugFrequency").value.trim();
|
||||||
|
const impactCore = document.getElementById("impactCore").value.trim();
|
||||||
|
const additionalNotes = document.getElementById("additionalNotes").value.trim();
|
||||||
|
|
||||||
|
// Construct the bug report content as Markdown.
|
||||||
|
const bugReportContent = `
|
||||||
|
# **bitvid Bug Report Form**
|
||||||
|
|
||||||
|
If you have encountered a bug or technical issue on bitvid, please fill out this form to help us diagnose and resolve the problem. Providing detailed information will assist us in troubleshooting more efficiently.
|
||||||
|
|
||||||
|
## **1. User Information**
|
||||||
|
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||||
|
- **Roles:** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
||||||
|
|
||||||
|
## **2. Bug Details**
|
||||||
|
- **Describe the issue:**
|
||||||
|
${issueDescription || "N/A"}
|
||||||
|
- **Steps to reproduce the bug:**
|
||||||
|
${stepsToReproduce || "N/A"}
|
||||||
|
- **Expected behavior:**
|
||||||
|
${expectedBehavior || "N/A"}
|
||||||
|
- **Actual behavior:**
|
||||||
|
${actualBehavior || "N/A"}
|
||||||
|
|
||||||
|
## **3. Device & Environment**
|
||||||
|
- **Devices used:** ${devicesUsed.length > 0 ? devicesUsed.join(", ") : "N/A"}
|
||||||
|
- **Operating System:** ${operatingSystem || "N/A"}
|
||||||
|
- **Browser:** ${browserInfo || "N/A"}
|
||||||
|
- **Using VPN/Privacy Settings:** ${usingVPN || "N/A"}
|
||||||
|
|
||||||
|
## **4. Screenshots or Logs**
|
||||||
|
- **Screenshot/Recording:** ${screenshotInfo || "N/A"}
|
||||||
|
- **Error Messages:** ${errorMessages || "N/A"}
|
||||||
|
- **Console Logs:** ${consoleLogs || "N/A"}
|
||||||
|
|
||||||
|
## **5. Additional Information**
|
||||||
|
- **Bug Frequency:** ${bugFrequency || "N/A"}
|
||||||
|
- **Impact on Core Functionality:** ${impactCore || "N/A"}
|
||||||
|
- **Additional Notes:** ${additionalNotes || "N/A"}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Processing & Resolution:**
|
||||||
|
Our team reviews bug reports regularly. While we aim to fix critical issues as soon as possible, some bugs may take longer to resolve. We appreciate your patience and help in improving bitvid!
|
||||||
|
|
||||||
|
For urgent issues, contact us through bitvid’s Nostr support channels.
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
log("[DEBUG] Constructed bug report content:\n" + bugReportContent);
|
||||||
|
|
||||||
|
// Decode the recipient NPUB to get the public key.
|
||||||
|
log("Decoding recipient npub...");
|
||||||
|
const decoded = nip19.decode(recipientNpub);
|
||||||
|
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Generate an ephemeral key pair.
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey();
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv);
|
||||||
|
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Encrypt the bug report content.
|
||||||
|
log("Encrypting bug report content (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, bugReportContent);
|
||||||
|
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// Build the event template.
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const eventTemplate = {
|
||||||
|
kind: 4,
|
||||||
|
created_at: now,
|
||||||
|
tags: [["p", targetPubHex]],
|
||||||
|
content: ciphertext,
|
||||||
|
};
|
||||||
|
log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
|
||||||
|
|
||||||
|
// Finalize the event.
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||||
|
|
||||||
|
// Publish the event to all relays.
|
||||||
|
log("Publishing the bug report to relays...");
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// Subscribe to each relay.
|
||||||
|
for (const url of RELAYS) {
|
||||||
|
log("Connecting to " + url + " for subscription...");
|
||||||
|
const relay = await Relay.connect(url);
|
||||||
|
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
|
||||||
|
onEvent(foundEvent) {
|
||||||
|
if (foundEvent.id === event.id) {
|
||||||
|
log("[" + url + "] => Found our bug report in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Done. If the logs show that at least one relay accepted the event and the bug report was found in storage, our team will review your report within 7-14 days.");
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
363
src/components/iframe_forms/iframe-content-appeals-form.html
Normal file
363
src/components/iframe_forms/iframe-content-appeals-form.html
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>bitvid Content Appeals Form</title>
|
||||||
|
<!-- Link to your main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<style>
|
||||||
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
|
/* Remove width constraints from body so our container can be full width */
|
||||||
|
body {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override the .container to use the full available width */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card-like container for the form, similar to modal-content */
|
||||||
|
.form-container {
|
||||||
|
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels in a light gray */
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input, textarea, and select mimic modal field styles */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
||||||
|
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
||||||
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #3B82F6; /* blue-500 */
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 1px #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styled similarly to modal publish button */
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3B82F6; /* blue-500 */
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status log area */
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111827;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #F87171; /* a red tint */
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #3B82F6; /* blue-500 */
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #FACC15; /* a yellow tone */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
/* Custom Scrollbar styling for Firefox */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3B82F6 transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<p>
|
||||||
|
If you believe your content was unfairly blocked or restricted on bitvid,
|
||||||
|
please complete this form. Appeals will be reviewed manually, and
|
||||||
|
decisions will be communicated back to you.
|
||||||
|
</p>
|
||||||
|
<form id="dm-form">
|
||||||
|
<!-- NPUB input removed as the recipient is now set by the code -->
|
||||||
|
<h2>1. User Information</h2>
|
||||||
|
<label for="contactMethod">Contact Method (if applicable):</label>
|
||||||
|
<input type="text" id="contactMethod" placeholder="Nostr DM, email, or other" />
|
||||||
|
|
||||||
|
<h2>2. Content Details</h2>
|
||||||
|
<label for="videoTitle">Title of the Video:</label>
|
||||||
|
<input type="text" id="videoTitle" placeholder="Enter the exact title" />
|
||||||
|
|
||||||
|
<label for="magnetLink">Magnet Link:</label>
|
||||||
|
<input type="text" id="magnetLink" placeholder="Enter the magnet link" />
|
||||||
|
|
||||||
|
<label for="submissionDate">Date of Content Submission:</label>
|
||||||
|
<input type="date" id="submissionDate" />
|
||||||
|
|
||||||
|
<h2>3. Reason for Appeal</h2>
|
||||||
|
<label for="reasonBlocked">Why do you believe your content was unfairly blocked?</label>
|
||||||
|
<textarea id="reasonBlocked" rows="3" placeholder="Explain in detail"></textarea>
|
||||||
|
|
||||||
|
<label for="fitsGuidelines">Does your content fit within bitvid's Community Guidelines?</label>
|
||||||
|
<select id="fitsGuidelines">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="guidelinesCited">If yes, which guideline(s) support your appeal?</label>
|
||||||
|
<textarea id="guidelinesCited" rows="2" placeholder="Cite the specific guidelines"></textarea>
|
||||||
|
|
||||||
|
<label for="editedContent">Was this content edited after being blocked?</label>
|
||||||
|
<select id="editedContent">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="changesMade">If yes, what changes were made?</label>
|
||||||
|
<textarea id="changesMade" rows="2" placeholder="Describe the modifications"></textarea>
|
||||||
|
|
||||||
|
<h2>4. Additional Context</h2>
|
||||||
|
<label for="misunderstanding">Was there any misunderstanding or misclassification?</label>
|
||||||
|
<textarea id="misunderstanding" rows="2" placeholder="Provide context"></textarea>
|
||||||
|
|
||||||
|
<label for="externalReferences">Are there external references that validate your appeal?</label>
|
||||||
|
<textarea id="externalReferences" rows="2" placeholder="Links, citations, or additional info"></textarea>
|
||||||
|
|
||||||
|
<h2>5. Declaration</h2>
|
||||||
|
<p>
|
||||||
|
By submitting this appeal, you confirm that:
|
||||||
|
<br />- You are the original creator or authorized representative of the content.
|
||||||
|
<br />- Your appeal is submitted in good faith and aligns with bitvid’s policies.
|
||||||
|
<br />- You understand that final decisions are at the discretion of bitvid’s moderation process.
|
||||||
|
</p>
|
||||||
|
<label for="signature">Signature (Digital or Written):</label>
|
||||||
|
<input type="text" id="signature" placeholder="Your signature" />
|
||||||
|
|
||||||
|
<label for="declarationDate">Date:</label>
|
||||||
|
<input type="date" id="declarationDate" />
|
||||||
|
|
||||||
|
<button type="submit">Submit Appeal</button>
|
||||||
|
</form>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging functions for both on-page and console output.
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
||||||
|
|
||||||
|
// Set the recipient's NPUB (your personal NPUB) here.
|
||||||
|
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net"
|
||||||
|
];
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
document.getElementById("dm-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
try {
|
||||||
|
// Note: The customer's NPUB input is removed.
|
||||||
|
// Use the recipientNpub constant to get the public key.
|
||||||
|
|
||||||
|
const contactMethod = document.getElementById("contactMethod").value.trim();
|
||||||
|
const videoTitle = document.getElementById("videoTitle").value.trim();
|
||||||
|
const magnetLink = document.getElementById("magnetLink").value.trim();
|
||||||
|
const submissionDate = document.getElementById("submissionDate").value.trim();
|
||||||
|
const reasonBlocked = document.getElementById("reasonBlocked").value.trim();
|
||||||
|
const fitsGuidelines = document.getElementById("fitsGuidelines").value.trim();
|
||||||
|
const guidelinesCited = document.getElementById("guidelinesCited").value.trim();
|
||||||
|
const editedContent = document.getElementById("editedContent").value.trim();
|
||||||
|
const changesMade = document.getElementById("changesMade").value.trim();
|
||||||
|
const misunderstanding = document.getElementById("misunderstanding").value.trim();
|
||||||
|
const externalReferences = document.getElementById("externalReferences").value.trim();
|
||||||
|
const signature = document.getElementById("signature").value.trim();
|
||||||
|
const declarationDate = document.getElementById("declarationDate").value.trim();
|
||||||
|
|
||||||
|
// Construct the appeal content.
|
||||||
|
const appealContent = `
|
||||||
|
# **bitvid Content Appeals Form**
|
||||||
|
|
||||||
|
**1. User Information**
|
||||||
|
- **Contact Method:** ${contactMethod || "N/A"}
|
||||||
|
|
||||||
|
**2. Content Details**
|
||||||
|
- **Title of the Video:** ${videoTitle}
|
||||||
|
- **Magnet Link:** ${magnetLink}
|
||||||
|
- **Date of Content Submission:** ${submissionDate}
|
||||||
|
|
||||||
|
**3. Reason for Appeal**
|
||||||
|
- **Why do you believe your content was unfairly blocked?**
|
||||||
|
${reasonBlocked}
|
||||||
|
- **Does your content fit within bitvid's Community Guidelines?**
|
||||||
|
${fitsGuidelines}
|
||||||
|
- **If yes, which guideline(s) support your appeal?**
|
||||||
|
${guidelinesCited}
|
||||||
|
- **Was this content edited after being blocked?**
|
||||||
|
${editedContent}
|
||||||
|
- **If yes, what changes were made?**
|
||||||
|
${changesMade}
|
||||||
|
|
||||||
|
**4. Additional Context**
|
||||||
|
- **Was there any misunderstanding or misclassification?**
|
||||||
|
${misunderstanding}
|
||||||
|
- **Are there external references that validate your appeal?**
|
||||||
|
${externalReferences}
|
||||||
|
|
||||||
|
**5. Declaration**
|
||||||
|
By submitting this appeal, you confirm that:
|
||||||
|
- You are the original creator or an authorized representative of the content.
|
||||||
|
- Your appeal is submitted in good faith and aligns with bitvid’s policies.
|
||||||
|
- You understand that final decisions are at the discretion of bitvid’s moderation process.
|
||||||
|
|
||||||
|
**Signature (Digital or Written):** ${signature}
|
||||||
|
**Date:** ${declarationDate}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Processing Time:** Appeals will be reviewed within **7-14 days**. If additional information is required, you will be contacted via your provided contact method.
|
||||||
|
|
||||||
|
For further questions, reach out through bitvid’s Nostr support channels.
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
log("[DEBUG] Constructed appeal content:\n" + appealContent);
|
||||||
|
|
||||||
|
// Decode the recipient NPUB to get the public key.
|
||||||
|
log("Decoding recipient npub...");
|
||||||
|
const decoded = nip19.decode(recipientNpub);
|
||||||
|
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Generate an ephemeral key pair.
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey();
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv);
|
||||||
|
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Encrypt the appeal content.
|
||||||
|
log("Encrypting appeal content (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, appealContent);
|
||||||
|
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// Build the event template.
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const eventTemplate = {
|
||||||
|
kind: 4,
|
||||||
|
created_at: now,
|
||||||
|
tags: [["p", targetPubHex]],
|
||||||
|
content: ciphertext,
|
||||||
|
};
|
||||||
|
log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
|
||||||
|
|
||||||
|
// Finalize the event.
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||||
|
|
||||||
|
// Publish the event to all relays.
|
||||||
|
log("Publishing the appeal to relays...");
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// Subscribe to each relay.
|
||||||
|
for (const url of RELAYS) {
|
||||||
|
log("Connecting to " + url + " for subscription...");
|
||||||
|
const relay = await Relay.connect(url);
|
||||||
|
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
|
||||||
|
onEvent(foundEvent) {
|
||||||
|
if (foundEvent.id === event.id) {
|
||||||
|
log("[" + url + "] => Found our appeal in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Done. If the logs show that at least one relay accepted the event and the appeal was found in storage, a moderator will review your appeal within 7-14 days.");
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
345
src/components/iframe_forms/iframe-feedback-form.html
Normal file
345
src/components/iframe_forms/iframe-feedback-form.html
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>bitvid General Feedback Form</title>
|
||||||
|
<!-- Link to your main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<style>
|
||||||
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
|
/* Remove width constraints from body so our container can be full width */
|
||||||
|
body {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override the .container to use the full available width */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card-like container for the form */
|
||||||
|
.form-container {
|
||||||
|
background-color: #111827; /* Tailwind's bg-gray-900 */
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels in a light gray */
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E5E7EB; /* Tailwind's text-gray-200 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input, textarea, and select styling */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background-color: #1F2937; /* Tailwind's bg-gray-800 */
|
||||||
|
color: #F3F4F6; /* Tailwind's text-gray-100 */
|
||||||
|
border: 1px solid #374151; /* Tailwind's border-gray-700 */
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #3B82F6; /* blue-500 */
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 1px #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox group styling */
|
||||||
|
.checkbox-group label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio group styling (for experience rating) */
|
||||||
|
.radio-group label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styling */
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3B82F6; /* blue-500 */
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status log area */
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111827;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #F87171;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #FACC15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar styling for WebKit browsers */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
/* Custom Scrollbar styling for Firefox */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3B82F6 transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<p>
|
||||||
|
Your feedback helps us improve bitvid! Whether it’s a suggestion, a concern, or general thoughts on the platform, we’d love to hear from you.
|
||||||
|
</p>
|
||||||
|
<form id="feedback-form">
|
||||||
|
<!-- Section 1: User Information -->
|
||||||
|
<h2>1. User Information</h2>
|
||||||
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
|
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
||||||
|
<p>Are you a (check all that apply):</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 2: General Feedback -->
|
||||||
|
<h2>2. General Feedback</h2>
|
||||||
|
<label>How would you rate your experience on bitvid so far?</label>
|
||||||
|
<div class="radio-group">
|
||||||
|
<label><input type="radio" name="experienceRating" value="Excellent" /> Excellent</label>
|
||||||
|
<label><input type="radio" name="experienceRating" value="Good" /> Good</label>
|
||||||
|
<label><input type="radio" name="experienceRating" value="Average" /> Average</label>
|
||||||
|
<label><input type="radio" name="experienceRating" value="Needs Improvement" /> Needs Improvement</label>
|
||||||
|
</div>
|
||||||
|
<label for="likeMost">What do you like most about bitvid?</label>
|
||||||
|
<textarea id="likeMost" rows="3" placeholder="Describe the features, usability, or content you enjoy"></textarea>
|
||||||
|
<label for="improvements">What would you like to see improved?</label>
|
||||||
|
<textarea id="improvements" rows="3" placeholder="Provide specific areas for improvement"></textarea>
|
||||||
|
<label for="confusingFeatures">Are there any features or tools you find confusing or difficult to use?</label>
|
||||||
|
<textarea id="confusingFeatures" rows="3" placeholder="Explain any challenges you’ve encountered"></textarea>
|
||||||
|
|
||||||
|
<!-- Section 3: Additional Comments -->
|
||||||
|
<h2>3. Additional Comments</h2>
|
||||||
|
<label for="otherSuggestions">Do you have any other suggestions or thoughts about bitvid?</label>
|
||||||
|
<textarea id="otherSuggestions" rows="3" placeholder="Share any other feedback"></textarea>
|
||||||
|
<label for="followUp">Would you like to be contacted for follow-up discussions?</label>
|
||||||
|
<select id="followUp">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
<label for="preferredContact">Preferred contact method (if applicable):</label>
|
||||||
|
<input type="text" id="preferredContact" placeholder="Nostr DM, email, or other" />
|
||||||
|
|
||||||
|
<button type="submit">Submit General Feedback</button>
|
||||||
|
</form>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging function for on-page and console output.
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
||||||
|
|
||||||
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
|
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net"
|
||||||
|
];
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
document.getElementById("feedback-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Section 1: User Information
|
||||||
|
const userNpub = document.getElementById("userNpub").value.trim();
|
||||||
|
const roleNodes = document.querySelectorAll('input[name="userRole"]:checked');
|
||||||
|
let userRoles = [];
|
||||||
|
roleNodes.forEach(node => {
|
||||||
|
userRoles.push(node.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Section 2: General Feedback
|
||||||
|
const experienceRadio = document.querySelector('input[name="experienceRating"]:checked');
|
||||||
|
const experienceRating = experienceRadio ? experienceRadio.value : "N/A";
|
||||||
|
const likeMost = document.getElementById("likeMost").value.trim();
|
||||||
|
const improvements = document.getElementById("improvements").value.trim();
|
||||||
|
const confusingFeatures = document.getElementById("confusingFeatures").value.trim();
|
||||||
|
|
||||||
|
// Section 3: Additional Comments
|
||||||
|
const otherSuggestions = document.getElementById("otherSuggestions").value.trim();
|
||||||
|
const followUp = document.getElementById("followUp").value.trim();
|
||||||
|
const preferredContact = document.getElementById("preferredContact").value.trim();
|
||||||
|
|
||||||
|
// Construct the Markdown feedback content
|
||||||
|
const feedbackContent = `
|
||||||
|
# **bitvid General Feedback Form**
|
||||||
|
|
||||||
|
Your feedback helps us improve bitvid! Whether it’s a suggestion, a concern, or general thoughts on the platform, we’d love to hear from you.
|
||||||
|
|
||||||
|
## **1. User Information**
|
||||||
|
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||||
|
- **Are you a (check all that apply):** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
||||||
|
|
||||||
|
## **2. General Feedback**
|
||||||
|
- **How would you rate your experience on bitvid so far?** ${experienceRating}
|
||||||
|
- **What do you like most about bitvid?**
|
||||||
|
${likeMost || "N/A"}
|
||||||
|
- **What would you like to see improved?**
|
||||||
|
${improvements || "N/A"}
|
||||||
|
- **Are there any features or tools you find confusing or difficult to use?**
|
||||||
|
${confusingFeatures || "N/A"}
|
||||||
|
|
||||||
|
## **3. Additional Comments**
|
||||||
|
- **Do you have any other suggestions or thoughts about bitvid?**
|
||||||
|
${otherSuggestions || "N/A"}
|
||||||
|
- **Would you like to be contacted for follow-up discussions?** ${followUp || "N/A"}
|
||||||
|
- **Preferred contact method (if applicable):** ${preferredContact || "N/A"}
|
||||||
|
|
||||||
|
---
|
||||||
|
### **Processing & Consideration**
|
||||||
|
We review all feedback regularly to improve bitvid. While not all suggestions may be implemented, we greatly appreciate your input and strive to enhance the platform based on community insights.
|
||||||
|
|
||||||
|
For additional discussions, reach out via bitvid’s Nostr support channels.
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
log("[DEBUG] Constructed feedback content:\n" + feedbackContent);
|
||||||
|
|
||||||
|
// Decode the recipient NPUB to get the public key.
|
||||||
|
log("Decoding recipient npub...");
|
||||||
|
const decoded = nip19.decode(recipientNpub);
|
||||||
|
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Generate an ephemeral key pair.
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey();
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv);
|
||||||
|
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Encrypt the feedback content.
|
||||||
|
log("Encrypting feedback content (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, feedbackContent);
|
||||||
|
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// Build the event template.
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const eventTemplate = {
|
||||||
|
kind: 4,
|
||||||
|
created_at: now,
|
||||||
|
tags: [["p", targetPubHex]],
|
||||||
|
content: ciphertext,
|
||||||
|
};
|
||||||
|
log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
|
||||||
|
|
||||||
|
// Finalize the event.
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||||
|
|
||||||
|
// Publish the event to all relays.
|
||||||
|
log("Publishing the feedback to relays...");
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// Subscribe to each relay to verify storage.
|
||||||
|
for (const url of RELAYS) {
|
||||||
|
log("Connecting to " + url + " for subscription...");
|
||||||
|
const relay = await Relay.connect(url);
|
||||||
|
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
|
||||||
|
onEvent(foundEvent) {
|
||||||
|
if (foundEvent.id === event.id) {
|
||||||
|
log("[" + url + "] => Found our feedback in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Done. If the logs show that at least one relay accepted the event and the feedback was stored, it will be reviewed accordingly.");
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
357
src/components/iframe_forms/iframe-request-form.html
Normal file
357
src/components/iframe_forms/iframe-request-form.html
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>bitvid Feature Request Form</title>
|
||||||
|
<!-- Link to your main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<style>
|
||||||
|
/* Override for form page to match modal field styling */
|
||||||
|
|
||||||
|
/* Remove width constraints from body so our container can be full width */
|
||||||
|
body {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full-width container */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card-like container for the form */
|
||||||
|
.form-container {
|
||||||
|
background-color: #111827;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels */
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input, textarea, and select styling */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background-color: #1F2937;
|
||||||
|
color: #F3F4F6;
|
||||||
|
border: 1px solid #374151;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #3B82F6;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 1px #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox group styling */
|
||||||
|
.checkbox-group label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styling */
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3B82F6;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status log area */
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111827;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #F87171;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #FACC15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3B82F6 transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<p>
|
||||||
|
Have an idea for improving bitvid? We’d love to hear it! Please use this form to request new features or enhancements. Your feedback helps shape the future of bitvid.
|
||||||
|
</p>
|
||||||
|
<form id="feature-form">
|
||||||
|
<!-- Section 1: User Information -->
|
||||||
|
<h2>1. User Information</h2>
|
||||||
|
<label for="userNpub">Nostr Public Key (npub) (optional):</label>
|
||||||
|
<input type="text" id="userNpub" placeholder="Enter your npub (optional)" />
|
||||||
|
<p>Are you a (check all that apply):</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="userRole" value="Viewer" /> Viewer</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Content Creator" /> Content Creator</label>
|
||||||
|
<label><input type="checkbox" name="userRole" value="Developer/Contributor" /> Developer / Contributor</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 2: Feature Request Details -->
|
||||||
|
<h2>2. Feature Request Details</h2>
|
||||||
|
<label for="featureName">Feature Name:</label>
|
||||||
|
<input type="text" id="featureName" placeholder="Short, descriptive title" required />
|
||||||
|
<label for="featureDescription">Describe the feature:</label>
|
||||||
|
<textarea id="featureDescription" rows="3" placeholder="Explain in detail what the feature does and how it works"></textarea>
|
||||||
|
<label for="featureImportance">Why is this feature important?</label>
|
||||||
|
<textarea id="featureImportance" rows="2" placeholder="Describe how this will improve the platform and benefit users"></textarea>
|
||||||
|
<label for="beneficiary">Who would benefit from this feature?</label>
|
||||||
|
<input type="text" id="beneficiary" placeholder="e.g., Content Creators, Viewers, Both" />
|
||||||
|
|
||||||
|
<!-- Section 3: Additional Information -->
|
||||||
|
<h2>3. Additional Information</h2>
|
||||||
|
<label for="existingPlatforms">Are there existing platforms that have this feature?</label>
|
||||||
|
<textarea id="existingPlatforms" rows="2" placeholder="Provide examples if applicable"></textarea>
|
||||||
|
<label for="mockups">Do you have any mockups or examples?</label>
|
||||||
|
<textarea id="mockups" rows="2" placeholder="Links, screenshots, or diagrams"></textarea>
|
||||||
|
<label for="willingToTest">Would you be willing to help test this feature if implemented?</label>
|
||||||
|
<select id="willingToTest">
|
||||||
|
<option value="">Select an option</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Section 4: Priority & Impact -->
|
||||||
|
<h2>4. Priority & Impact</h2>
|
||||||
|
<p>How urgent is this feature?</p>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label><input type="checkbox" name="featurePriority" value="High" /> High (Essential for platform success)</label>
|
||||||
|
<label><input type="checkbox" name="featurePriority" value="Medium" /> Medium (Would improve experience significantly)</label>
|
||||||
|
<label><input type="checkbox" name="featurePriority" value="Low" /> Low (Nice to have, but not urgent)</label>
|
||||||
|
</div>
|
||||||
|
<label for="techChallenges">Does this feature require technical expertise to implement?</label>
|
||||||
|
<textarea id="techChallenges" rows="2" placeholder="Describe any dependencies or challenges"></textarea>
|
||||||
|
|
||||||
|
<button type="submit">Submit Feature Request</button>
|
||||||
|
</form>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging function
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { generateSecretKey, getPublicKey, finalizeEvent, nip04, nip19, SimplePool, Relay } = window.NostrTools;
|
||||||
|
|
||||||
|
// Set the recipient's NPUB (your personal NPUB)
|
||||||
|
const recipientNpub = "npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net"
|
||||||
|
];
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
document.getElementById("feature-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
try {
|
||||||
|
// Section 1: User Information
|
||||||
|
const userNpub = document.getElementById("userNpub").value.trim();
|
||||||
|
const roleNodes = document.querySelectorAll('input[name="userRole"]:checked');
|
||||||
|
let userRoles = [];
|
||||||
|
roleNodes.forEach(node => {
|
||||||
|
userRoles.push(node.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Section 2: Feature Request Details
|
||||||
|
const featureName = document.getElementById("featureName").value.trim();
|
||||||
|
const featureDescription = document.getElementById("featureDescription").value.trim();
|
||||||
|
const featureImportance = document.getElementById("featureImportance").value.trim();
|
||||||
|
const beneficiary = document.getElementById("beneficiary").value.trim();
|
||||||
|
|
||||||
|
// Section 3: Additional Information
|
||||||
|
const existingPlatforms = document.getElementById("existingPlatforms").value.trim();
|
||||||
|
const mockups = document.getElementById("mockups").value.trim();
|
||||||
|
const willingToTest = document.getElementById("willingToTest").value.trim();
|
||||||
|
|
||||||
|
// Section 4: Priority & Impact
|
||||||
|
const priorityNodes = document.querySelectorAll('input[name="featurePriority"]:checked');
|
||||||
|
let featurePriorities = [];
|
||||||
|
priorityNodes.forEach(node => {
|
||||||
|
featurePriorities.push(node.value);
|
||||||
|
});
|
||||||
|
const techChallenges = document.getElementById("techChallenges").value.trim();
|
||||||
|
|
||||||
|
// Construct Markdown feature request
|
||||||
|
const featureRequestContent = `
|
||||||
|
# **bitvid Feature Request Form**
|
||||||
|
|
||||||
|
Have an idea for improving bitvid? We’d love to hear it! Please use this form to request new features or enhancements. Your feedback helps shape the future of bitvid.
|
||||||
|
|
||||||
|
## **1. User Information**
|
||||||
|
- **Nostr Public Key (npub) (optional):** ${userNpub || "N/A"}
|
||||||
|
- **Roles:** ${userRoles.length > 0 ? userRoles.join(", ") : "N/A"}
|
||||||
|
|
||||||
|
## **2. Feature Request Details**
|
||||||
|
- **Feature Name:** ${featureName || "N/A"}
|
||||||
|
- **Describe the feature:**
|
||||||
|
${featureDescription || "N/A"}
|
||||||
|
- **Why is this feature important?**
|
||||||
|
${featureImportance || "N/A"}
|
||||||
|
- **Who would benefit from this feature?**
|
||||||
|
${beneficiary || "N/A"}
|
||||||
|
|
||||||
|
## **3. Additional Information**
|
||||||
|
- **Are there existing platforms that have this feature?**
|
||||||
|
${existingPlatforms || "N/A"}
|
||||||
|
- **Do you have any mockups or examples?**
|
||||||
|
${mockups || "N/A"}
|
||||||
|
- **Would you be willing to help test this feature if implemented?** ${willingToTest || "N/A"}
|
||||||
|
|
||||||
|
## **4. Priority & Impact**
|
||||||
|
- **How urgent is this feature?**
|
||||||
|
${featurePriorities.length > 0 ? featurePriorities.join(", ") : "N/A"}
|
||||||
|
- **Does this feature require technical expertise to implement?**
|
||||||
|
${techChallenges || "N/A"}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Processing & Consideration**
|
||||||
|
All feature requests are reviewed, but not all may be implemented. We prioritize based on user demand, feasibility, and impact on bitvid. Thank you for helping us improve!
|
||||||
|
|
||||||
|
For further discussions, reach out through bitvid’s Nostr support channels.
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
log("[DEBUG] Constructed feature request content:\n" + featureRequestContent);
|
||||||
|
|
||||||
|
// Decode the recipient NPUB
|
||||||
|
log("Decoding recipient npub...");
|
||||||
|
const decoded = nip19.decode(recipientNpub);
|
||||||
|
log("[DEBUG] Decoded npub: " + JSON.stringify(decoded));
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log("Recipient pubkey: " + targetPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Generate an ephemeral key pair
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey();
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv);
|
||||||
|
log("Ephemeral pubkey: " + ephemeralPubHex.slice(0, 16) + "...");
|
||||||
|
|
||||||
|
// Encrypt the feature request content
|
||||||
|
log("Encrypting feature request content (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(ephemeralPriv, targetPubHex, featureRequestContent);
|
||||||
|
log("[DEBUG] Ciphertext: " + ciphertext);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// Build the event template
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const eventTemplate = {
|
||||||
|
kind: 4,
|
||||||
|
created_at: now,
|
||||||
|
tags: [["p", targetPubHex]],
|
||||||
|
content: ciphertext,
|
||||||
|
};
|
||||||
|
log("[DEBUG] Event template before finalizing: " + JSON.stringify(eventTemplate));
|
||||||
|
|
||||||
|
// Finalize the event
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log("[DEBUG] Final event: " + JSON.stringify(event));
|
||||||
|
|
||||||
|
// Publish to relays
|
||||||
|
log("Publishing the feature request to relays...");
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// Subscribe to verify event storage
|
||||||
|
for (const url of RELAYS) {
|
||||||
|
log("Connecting to " + url + " for subscription...");
|
||||||
|
const relay = await Relay.connect(url);
|
||||||
|
relay.subscribe([{ authors: [ephemeralPubHex], kinds: [4] }], {
|
||||||
|
onEvent(foundEvent) {
|
||||||
|
if (foundEvent.id === event.id) {
|
||||||
|
log("[" + url + "] => Found our feature request in storage! ID: " + foundEvent.id.slice(0, 8) + "...", "success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Done. If the logs show that at least one relay accepted the event and the feature request was stored, it will be reviewed accordingly.");
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -3,7 +3,7 @@
|
|||||||
class="fixed inset-0 z-50 hidden"
|
class="fixed inset-0 z-50 hidden"
|
||||||
style="background: transparent"
|
style="background: transparent"
|
||||||
>
|
>
|
||||||
<!-- Dark overlay -->
|
<!-- Dark overlay with blur (unchanged) -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 z-10"
|
class="absolute inset-0 z-10"
|
||||||
style="
|
style="
|
||||||
@@ -13,14 +13,14 @@
|
|||||||
"
|
"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Modal container -->
|
<!-- Outer container matching video modal's flex layout & wide sizing -->
|
||||||
<div
|
<div
|
||||||
class="relative modal-container h-full w-full flex items-center justify-center overflow-y-auto z-20"
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="modal-content bg-gray-900 w-full max-w-md my-0 rounded-lg overflow-hidden relative max-h-[90vh]"
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
>
|
>
|
||||||
<!-- Header -->
|
<!-- Header with the special exit button (unchanged) -->
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between"
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between"
|
||||||
>
|
>
|
||||||
@@ -46,8 +46,20 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Body with login buttons -->
|
<!-- Body with buttons (unchanged) -->
|
||||||
<div class="p-6 space-y-4">
|
<div class="p-6 space-y-4">
|
||||||
|
<!-- Application Form button -->
|
||||||
|
<button
|
||||||
|
id="openApplicationModal"
|
||||||
|
class="w-full bg-indigo-500 text-white px-4 py-2 rounded-md hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
Application Form
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Simple horizontal rule -->
|
||||||
|
<div class="my-2 border-t border-gray-700"></div>
|
||||||
|
|
||||||
|
<!-- Existing login buttons -->
|
||||||
<button
|
<button
|
||||||
id="loginNIP07"
|
id="loginNIP07"
|
||||||
class="w-full bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
class="w-full bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||||
|
@@ -14,12 +14,14 @@
|
|||||||
"
|
"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Modal Container -->
|
<!-- Outer container with the same sizing approach as your other modals -->
|
||||||
<div
|
<div
|
||||||
class="relative modal-container h-full w-full flex items-start md:items-center justify-center overflow-y-auto z-20"
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
>
|
>
|
||||||
|
<!-- Inner content: wide layout, matching your video modal -->
|
||||||
<div
|
<div
|
||||||
class="modal-content bg-gray-900 w-full max-w-sm md:max-w-md my-0 rounded-lg overflow-hidden relative flex flex-col max-h-[90vh]"
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative flex flex-col"
|
||||||
|
style="max-height: 90vh"
|
||||||
>
|
>
|
||||||
<!-- Modal Header -->
|
<!-- Modal Header -->
|
||||||
<div
|
<div
|
||||||
|
@@ -19,7 +19,8 @@
|
|||||||
class="relative modal-container h-full w-full flex items-start md:items-center justify-center overflow-y-auto z-20"
|
class="relative modal-container h-full w-full flex items-start md:items-center justify-center overflow-y-auto z-20"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="modal-content bg-gray-900 w-full max-w-lg md:max-w-2xl my-0 rounded-lg overflow-hidden relative max-h-[90vh]"
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
style="max-height: 90vh"
|
||||||
>
|
>
|
||||||
<!-- Top Bar (similar to video-modal) -->
|
<!-- Top Bar (similar to video-modal) -->
|
||||||
<div
|
<div
|
||||||
|
@@ -323,7 +323,7 @@ footer a:hover {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
background-color: rgb(0 0 0 / 0.9);
|
background-color: rgb(0 0 0 / 0.9);
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
display: none; /* hidden by default */
|
/* remove display: none; */
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
@@ -331,7 +331,6 @@ footer a:hover {
|
|||||||
|
|
||||||
#disclaimerModal .modal-content {
|
#disclaimerModal .modal-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
@@ -376,16 +375,6 @@ footer a:hover {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#disclaimerModal .modal-content {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
max-width: 42rem;
|
|
||||||
max-height: 90vh;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#disclaimerModal .modal-scroll {
|
#disclaimerModal .modal-scroll {
|
||||||
max-height: calc(90vh - 5rem);
|
max-height: calc(90vh - 5rem);
|
||||||
}
|
}
|
||||||
|
427
src/index.html
427
src/index.html
@@ -95,6 +95,7 @@
|
|||||||
id="errorContainer"
|
id="errorContainer"
|
||||||
class="hidden bg-red-100 text-red-900 p-4 rounded-md mb-4"
|
class="hidden bg-red-100 text-red-900 p-4 rounded-md mb-4"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Success Container -->
|
<!-- Success Container -->
|
||||||
<div
|
<div
|
||||||
id="successContainer"
|
id="successContainer"
|
||||||
@@ -114,101 +115,162 @@
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Disclaimer Modal (inline markup; adjust as needed) -->
|
<!-- Disclaimer Modal (wide, matching other modals) -->
|
||||||
<div id="disclaimerModal" class="hidden">
|
<div
|
||||||
<div class="modal-content">
|
id="disclaimerModal"
|
||||||
<div class="modal-scroll">
|
class="fixed inset-0 z-50 hidden"
|
||||||
<div class="flex justify-center mb-8">
|
style="background: transparent"
|
||||||
<img
|
>
|
||||||
src="assets/svg/bitvid-logo-dark-mode.svg"
|
<!-- Dark/blur overlay -->
|
||||||
alt="BitVid Logo"
|
<div
|
||||||
class="h-16"
|
class="absolute inset-0 z-10"
|
||||||
/>
|
style="
|
||||||
</div>
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
<h2 class="text-2xl font-bold mb-4 text-center text-white">
|
backdrop-filter: blur(10px);
|
||||||
Welcome to bitvid
|
-webkit-backdrop-filter: blur(10px);
|
||||||
</h2>
|
"
|
||||||
<!-- Warning Alert -->
|
></div>
|
||||||
|
|
||||||
|
<!-- Outer container with wide layout, just like content-appeals-form -->
|
||||||
|
<div
|
||||||
|
class="modal-container relative h-full w-full flex items-start justify-center overflow-y-auto z-20"
|
||||||
|
>
|
||||||
|
<!-- The .modal-content, same classes: bg-gray-900, w-full max-w-[90%], etc. -->
|
||||||
|
<div
|
||||||
|
class="modal-content bg-gray-900 w-full max-w-[90%] lg:max-w-6xl my-0 rounded-lg overflow-hidden relative"
|
||||||
|
>
|
||||||
|
<!-- Sticky top bar, if you want a top heading or 'X' button up here -->
|
||||||
<div
|
<div
|
||||||
class="bg-yellow-900/20 border border-yellow-700/50 rounded-lg p-4 mb-6 flex items-start"
|
class="sticky top-0 bg-gradient-to-b from-black/80 to-transparent p-4 flex items-center justify-between"
|
||||||
>
|
>
|
||||||
<svg
|
<!-- If you want an X to close, you can add it here, for example:
|
||||||
class="h-5 w-5 text-yellow-500 mt-0.5 mr-3 flex-shrink-0"
|
<button
|
||||||
viewBox="0 0 24 24"
|
id="closeDisclaimerBtn"
|
||||||
fill="none"
|
class="flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 transition-all duration-200 backdrop-blur focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black focus:ring-blue-500"
|
||||||
stroke="currentColor"
|
|
||||||
>
|
>
|
||||||
<path
|
<svg
|
||||||
stroke-linecap="round"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
stroke-linejoin="round"
|
class="w-6 h-6 text-gray-300"
|
||||||
stroke-width="2"
|
fill="none"
|
||||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
viewBox="0 0 24 24"
|
||||||
/>
|
stroke="currentColor"
|
||||||
</svg>
|
>
|
||||||
<p class="text-yellow-200">
|
<path
|
||||||
This platform is currently in development and only supports
|
stroke-linecap="round"
|
||||||
Chrome and Firefox-based browsers. Other browsers are not
|
stroke-linejoin="round"
|
||||||
supported at this time. You may encounter bugs or missing
|
stroke-width="2"
|
||||||
features. Give it a sec. Videos might take 10 to 60 seconds to
|
d="M6 18L18 6M6 6l12 12"
|
||||||
load initially.
|
/>
|
||||||
</p>
|
</svg>
|
||||||
|
</button>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-6 text-gray-300">
|
|
||||||
<p>
|
<!-- Main Content -->
|
||||||
bitvid is a decentralized video platform where content is shared
|
<div class="p-6">
|
||||||
directly between users. We want you to understand a few
|
<!-- Scrollable disclaimers area -->
|
||||||
important points before you continue:
|
<div
|
||||||
</p>
|
class="space-y-6 text-gray-300"
|
||||||
<div class="space-y-4">
|
style="max-height: 70vh; overflow-y: auto"
|
||||||
<div class="bg-gray-800 rounded-lg p-4">
|
>
|
||||||
<h3 class="text-white font-semibold mb-2">
|
<!-- Example: Insert your disclaimers here -->
|
||||||
Early Access Status
|
|
||||||
</h3>
|
<div class="flex justify-center mb-8">
|
||||||
<p class="text-gray-400">
|
<img
|
||||||
Currently, video posting is invite-only as we carefully
|
src="assets/svg/bitvid-logo-dark-mode.svg"
|
||||||
scale our platform. While anyone can watch videos, content
|
alt="BitVid Logo"
|
||||||
creation is limited to approved creators. This helps us
|
class="h-16"
|
||||||
maintain quality content during our early stages.
|
/>
|
||||||
|
</div>
|
||||||
|
<h2 class="text-2xl font-bold text-white mb-0">
|
||||||
|
Welcome to bitvid
|
||||||
|
</h2>
|
||||||
|
<!-- Warning Alert -->
|
||||||
|
<div
|
||||||
|
class="bg-yellow-900/20 border border-yellow-700/50 rounded-lg p-4 mb-6 flex items-start"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="h-5 w-5 text-yellow-500 mt-0.5 mr-3 flex-shrink-0"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<p class="text-yellow-200">
|
||||||
|
This platform is currently in development and only supports
|
||||||
|
Chrome and Firefox-based browsers. Other browsers are not
|
||||||
|
supported at this time. You may encounter bugs or missing
|
||||||
|
features. Give it a sec. Videos might take 10 to 60 seconds
|
||||||
|
to load initially.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-gray-800 rounded-lg p-4">
|
<p>
|
||||||
<h3 class="text-white font-semibold mb-2">
|
bitvid is a decentralized video platform where content is
|
||||||
Content Responsibility & Moderation
|
shared directly between users. We want you to understand a few
|
||||||
</h3>
|
important points before you continue:
|
||||||
<p class="text-gray-400">
|
</p>
|
||||||
While we don't host videos directly, we maintain community
|
|
||||||
standards through access control. Users who violate our
|
<div class="space-y-4">
|
||||||
guidelines may be blocked from accessing the platform. All
|
<div class="bg-gray-800 rounded-lg p-4">
|
||||||
content must follow local laws and platform guidelines.
|
<h3 class="text-white font-semibold mb-2">
|
||||||
</p>
|
Early Access Status
|
||||||
</div>
|
</h3>
|
||||||
<div class="bg-gray-800 rounded-lg p-4">
|
<p class="text-gray-400">
|
||||||
<h3 class="text-white font-semibold mb-2">Platform Status</h3>
|
Currently, video posting is invite-only as we carefully
|
||||||
<p class="text-gray-400">
|
scale our platform. While anyone can watch videos, content
|
||||||
bitvid is a work in progress. Features may change or break,
|
creation is limited to approved creators. This helps us
|
||||||
and security improvements are ongoing. Your feedback and
|
maintain quality content during our early stages.
|
||||||
patience help us build a better platform.
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div class="bg-gray-800 rounded-lg p-4">
|
||||||
<div class="bg-gray-800 rounded-lg p-4">
|
<h3 class="text-white font-semibold mb-2">
|
||||||
<h3 class="text-white font-semibold mb-2">Get Involved</h3>
|
Content Responsibility & Moderation
|
||||||
<p class="text-gray-400">
|
</h3>
|
||||||
Are you a developer? We'd love your help! Visit our GitHub
|
<p class="text-gray-400">
|
||||||
repository to contribute to building the future of
|
While we don't host videos directly, we maintain community
|
||||||
decentralized video sharing.
|
standards through access control. Users who violate our
|
||||||
</p>
|
guidelines may be blocked from accessing the platform. All
|
||||||
|
content must follow local laws and platform guidelines.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-800 rounded-lg p-4">
|
||||||
|
<h3 class="text-white font-semibold mb-2">
|
||||||
|
Platform Status
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-400">
|
||||||
|
bitvid is a work in progress. Features may change or
|
||||||
|
break, and security improvements are ongoing. Your
|
||||||
|
feedback and patience help us build a better platform.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-800 rounded-lg p-4">
|
||||||
|
<h3 class="text-white font-semibold mb-2">Get Involved</h3>
|
||||||
|
<p class="text-gray-400">
|
||||||
|
Are you a developer? We'd love your help! Visit our GitHub
|
||||||
|
repository to contribute to building the future of
|
||||||
|
decentralized video sharing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Button at bottom -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<button
|
||||||
|
id="acceptDisclaimer"
|
||||||
|
class="w-full bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-black transition-colors duration-200"
|
||||||
|
>
|
||||||
|
I Understand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-container">
|
|
||||||
<button
|
|
||||||
id="acceptDisclaimer"
|
|
||||||
class="w-full bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-900 transition-colors duration-200"
|
|
||||||
>
|
|
||||||
I Understand
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -325,7 +387,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Load external modal components -->
|
<!-- Load external modal components + attach event listeners -->
|
||||||
<script>
|
<script>
|
||||||
async function loadModal(url) {
|
async function loadModal(url) {
|
||||||
try {
|
try {
|
||||||
@@ -343,24 +405,199 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just load the login modal (or any others), without adding event listeners here.
|
Promise.all([
|
||||||
// The logic to open/close the modal is all in app.js now.
|
// Existing modals
|
||||||
Promise.all([loadModal("components/login-modal.html")]).then(() => {
|
loadModal("components/login-modal.html"),
|
||||||
console.log("Modals loaded (login-modal.html, etc.)");
|
loadModal("components/application-form.html"),
|
||||||
// Now that the login-modal is definitely in the DOM:
|
loadModal("components/content-appeals-form.html"),
|
||||||
const closeBtn = document.getElementById("closeLoginModal");
|
|
||||||
if (closeBtn) {
|
// New forms
|
||||||
closeBtn.addEventListener("click", () => {
|
loadModal("components/general-feedback-form.html"),
|
||||||
|
loadModal("components/feature-request-form.html"),
|
||||||
|
loadModal("components/bug-fix-form.html"),
|
||||||
|
]).then(() => {
|
||||||
|
console.log("Modals loaded.");
|
||||||
|
|
||||||
|
//
|
||||||
|
// 1) Login button => open login modal
|
||||||
|
//
|
||||||
|
const loginNavBtn = document.getElementById("loginButton");
|
||||||
|
if (loginNavBtn) {
|
||||||
|
loginNavBtn.addEventListener("click", () => {
|
||||||
|
const loginModal = document.getElementById("loginModal");
|
||||||
|
if (loginModal) {
|
||||||
|
loginModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 2) Close login modal
|
||||||
|
//
|
||||||
|
const closeLoginBtn = document.getElementById("closeLoginModal");
|
||||||
|
if (closeLoginBtn) {
|
||||||
|
closeLoginBtn.addEventListener("click", () => {
|
||||||
const loginModal = document.getElementById("loginModal");
|
const loginModal = document.getElementById("loginModal");
|
||||||
if (loginModal) {
|
if (loginModal) {
|
||||||
loginModal.classList.add("hidden");
|
loginModal.classList.add("hidden");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 3) “Application Form” button => open application form
|
||||||
|
//
|
||||||
|
const openAppFormBtn = document.getElementById("openApplicationModal");
|
||||||
|
if (openAppFormBtn) {
|
||||||
|
openAppFormBtn.addEventListener("click", () => {
|
||||||
|
// Hide the login modal first
|
||||||
|
const loginModal = document.getElementById("loginModal");
|
||||||
|
if (loginModal) {
|
||||||
|
loginModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
// Now show the application form modal
|
||||||
|
const appModal = document.getElementById("nostrFormModal");
|
||||||
|
if (appModal) {
|
||||||
|
appModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 4) Close application form
|
||||||
|
//
|
||||||
|
const closeNostrFormBtn = document.getElementById(
|
||||||
|
"closeNostrFormModal"
|
||||||
|
);
|
||||||
|
if (closeNostrFormBtn) {
|
||||||
|
closeNostrFormBtn.addEventListener("click", () => {
|
||||||
|
const appModal = document.getElementById("nostrFormModal");
|
||||||
|
if (appModal) {
|
||||||
|
appModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 5) ?modal=appeals => open content appeals form
|
||||||
|
//
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const modalParam = urlParams.get("modal");
|
||||||
|
if (modalParam === "appeals") {
|
||||||
|
const appealsModal = document.getElementById("contentAppealsModal");
|
||||||
|
if (appealsModal) {
|
||||||
|
appealsModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// 5.1) ?modal=application => open application form
|
||||||
|
//
|
||||||
|
else if (modalParam === "application") {
|
||||||
|
const appModal = document.getElementById("nostrFormModal");
|
||||||
|
if (appModal) {
|
||||||
|
appModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 6) Close content appeals modal
|
||||||
|
//
|
||||||
|
const closeAppealsBtn = document.getElementById(
|
||||||
|
"closeContentAppealsModal"
|
||||||
|
);
|
||||||
|
if (closeAppealsBtn) {
|
||||||
|
closeAppealsBtn.addEventListener("click", () => {
|
||||||
|
const appealsModal = document.getElementById("contentAppealsModal");
|
||||||
|
if (appealsModal) {
|
||||||
|
appealsModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 7) Show disclaimer modal on page load, hide on "I Understand"
|
||||||
|
//
|
||||||
|
const disclaimerModal = document.getElementById("disclaimerModal");
|
||||||
|
const acceptDisclaimerBtn = document.getElementById("acceptDisclaimer");
|
||||||
|
if (disclaimerModal) {
|
||||||
|
// Show immediately
|
||||||
|
disclaimerModal.classList.remove("hidden");
|
||||||
|
if (acceptDisclaimerBtn) {
|
||||||
|
acceptDisclaimerBtn.addEventListener("click", () => {
|
||||||
|
disclaimerModal.classList.add("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 8) Query param checks for the three new forms
|
||||||
|
//
|
||||||
|
// https://bitvid.network?modal=feedback => open generalFeedbackModal
|
||||||
|
// https://bitvid.network?modal=feature => open featureRequestModal
|
||||||
|
// https://bitvid.network?modal=bug => open bugFixModal
|
||||||
|
//
|
||||||
|
if (modalParam === "feedback") {
|
||||||
|
const feedbackModal = document.getElementById("generalFeedbackModal");
|
||||||
|
if (feedbackModal) {
|
||||||
|
feedbackModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
} else if (modalParam === "feature") {
|
||||||
|
const featureModal = document.getElementById("featureRequestModal");
|
||||||
|
if (featureModal) {
|
||||||
|
featureModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
} else if (modalParam === "bug") {
|
||||||
|
const bugModal = document.getElementById("bugFixModal");
|
||||||
|
if (bugModal) {
|
||||||
|
bugModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 9) Close buttons for the three new forms
|
||||||
|
//
|
||||||
|
// general feedback
|
||||||
|
const closeFeedbackBtn = document.getElementById(
|
||||||
|
"closeGeneralFeedbackModal"
|
||||||
|
);
|
||||||
|
if (closeFeedbackBtn) {
|
||||||
|
closeFeedbackBtn.addEventListener("click", () => {
|
||||||
|
const feedbackModal = document.getElementById(
|
||||||
|
"generalFeedbackModal"
|
||||||
|
);
|
||||||
|
if (feedbackModal) {
|
||||||
|
feedbackModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// feature request
|
||||||
|
const closeFeatureBtn = document.getElementById(
|
||||||
|
"closeFeatureRequestModal"
|
||||||
|
);
|
||||||
|
if (closeFeatureBtn) {
|
||||||
|
closeFeatureBtn.addEventListener("click", () => {
|
||||||
|
const featureModal = document.getElementById("featureRequestModal");
|
||||||
|
if (featureModal) {
|
||||||
|
featureModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// bug fix
|
||||||
|
const closeBugBtn = document.getElementById("closeBugFixModal");
|
||||||
|
if (closeBugBtn) {
|
||||||
|
closeBugBtn.addEventListener("click", () => {
|
||||||
|
const bugModal = document.getElementById("bugFixModal");
|
||||||
|
if (bugModal) {
|
||||||
|
bugModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Other Scripts -->
|
||||||
<script src="js/libs/nostr.bundle.js"></script>
|
<script src="js/libs/nostr.bundle.js"></script>
|
||||||
<script type="module" src="js/config.js"></script>
|
<script type="module" src="js/config.js"></script>
|
||||||
<script type="module" src="js/lists.js"></script>
|
<script type="module" src="js/lists.js"></script>
|
||||||
|
@@ -454,6 +454,23 @@ class bitvidApp {
|
|||||||
console.log("[popstate] user navigated back/forward; cleaning modal...");
|
console.log("[popstate] user navigated back/forward; cleaning modal...");
|
||||||
await this.hideModal();
|
await this.hideModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Event delegation for the “Application Form” button inside the login modal
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
if (event.target && event.target.id === "openApplicationModal") {
|
||||||
|
// 1) Hide the login modal
|
||||||
|
const loginModal = document.getElementById("loginModal");
|
||||||
|
if (loginModal) {
|
||||||
|
loginModal.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Show the application modal
|
||||||
|
const appModal = document.getElementById("nostrFormModal");
|
||||||
|
if (appModal) {
|
||||||
|
appModal.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,28 +2,35 @@ class DisclaimerModal {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.modal = document.getElementById("disclaimerModal");
|
this.modal = document.getElementById("disclaimerModal");
|
||||||
this.acceptButton = document.getElementById("acceptDisclaimer");
|
this.acceptButton = document.getElementById("acceptDisclaimer");
|
||||||
|
// If user previously dismissed the disclaimer, we'll store "true" in localStorage:
|
||||||
this.hasSeenDisclaimer = localStorage.getItem("hasSeenDisclaimer");
|
this.hasSeenDisclaimer = localStorage.getItem("hasSeenDisclaimer");
|
||||||
|
|
||||||
|
// Set up the click event for the "I Understand" button
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEventListeners() {
|
setupEventListeners() {
|
||||||
const closeModal = () => {
|
if (this.acceptButton) {
|
||||||
this.modal.style.display = "none";
|
this.acceptButton.addEventListener("click", () => {
|
||||||
document.body.style.overflow = "unset";
|
// Hide the disclaimer by adding the "hidden" class
|
||||||
localStorage.setItem("hasSeenDisclaimer", "true");
|
if (this.modal) {
|
||||||
};
|
this.modal.classList.add("hidden");
|
||||||
|
}
|
||||||
// Only keep the accept button event listener
|
// Mark that the user has seen the disclaimer, so we don't show it again
|
||||||
this.acceptButton.addEventListener("click", closeModal);
|
localStorage.setItem("hasSeenDisclaimer", "true");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
|
// Only show it if the user hasn't seen it before
|
||||||
if (!this.hasSeenDisclaimer) {
|
if (!this.hasSeenDisclaimer) {
|
||||||
this.modal.style.display = "flex";
|
if (this.modal) {
|
||||||
document.body.style.overflow = "hidden";
|
this.modal.classList.remove("hidden");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export an instance that you can import in your main script
|
||||||
export const disclaimerModal = new DisclaimerModal();
|
export const disclaimerModal = new DisclaimerModal();
|
||||||
|
263
src/tests/nostr-ephemeral-dm-tester.html
Normal file
263
src/tests/nostr-ephemeral-dm-tester.html
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Nostr Ephemeral DM Tester (Using finalizeEvent)</title>
|
||||||
|
<style>
|
||||||
|
/* Basic styling for the demo page */
|
||||||
|
body {
|
||||||
|
background: #222;
|
||||||
|
color: #eee;
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #888;
|
||||||
|
padding: 0.5em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background: #3399cc;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#status {
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #111;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
.status-line {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #ff6666;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #66ff66;
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
color: #ffff66;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Nostr Ephemeral DM Tester (Using finalizeEvent)
|
||||||
|
|
||||||
|
This HTML file demonstrates how to create a simple ephemeral DM tool using nostr‑tools v2.10.4.
|
||||||
|
|
||||||
|
Key steps:
|
||||||
|
|
||||||
|
1. Decode the target npub (using nip19.decode) to extract the target public key.
|
||||||
|
2. Generate an ephemeral key pair with generateSecretKey (returns Uint8Array) and getPublicKey (returns hex string).
|
||||||
|
3. Encrypt the message using nip04.encrypt (NIP‑04 encryption) with the ephemeral private key and the target public key.
|
||||||
|
4. Build an event template (a kind‑4 event) with the encrypted message and a tag containing the target pubkey.
|
||||||
|
5. Finalize the event using finalizeEvent, which computes the event id, assigns the ephemeral pubkey, and signs the event.
|
||||||
|
6. Publish the event to multiple relays using SimplePool.publish, which returns a promise – we use Promise.any to ensure at least one relay accepted the event.
|
||||||
|
7. Connect to each relay using the Relay API (Relay.connect) and subscribe for the event (using onEvent and onEose callbacks) to verify that it is stored.
|
||||||
|
|
||||||
|
Important lessons:
|
||||||
|
|
||||||
|
- finalizeEvent greatly simplifies the process by handling id computation and signing.
|
||||||
|
- The publish API now returns a promise; using Promise.any allows us to wait until one relay accepts the event.
|
||||||
|
- The subscription API has changed – use the Relay API instead of the deprecated pool.sub.
|
||||||
|
- Detailed logging is essential for debugging, so logs are output both to the page and the console.
|
||||||
|
|
||||||
|
References:
|
||||||
|
- Nostr-tools documentation: :contentReference[oaicite:0]{index=0}
|
||||||
|
- Nostr protocol specifications and community examples.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Load nostr‑tools v2.10.4 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@2.10.4/lib/nostr.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Nostr Ephemeral DM Tester (Using finalizeEvent)</h1>
|
||||||
|
<p>
|
||||||
|
Generates a random ephemeral key, encrypts your message (NIP‑04),
|
||||||
|
finalizes the event (computes event.id and signs it), then publishes it.
|
||||||
|
It also subscribes to each relay using the Relay API.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form id="dm-form">
|
||||||
|
<label for="npubInput">Target npub:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="npubInput"
|
||||||
|
placeholder="npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label for="msgInput">Message:</label>
|
||||||
|
<textarea
|
||||||
|
id="msgInput"
|
||||||
|
rows="3"
|
||||||
|
placeholder="Hello from ephemeral DM!"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
<button type="submit">Send DM</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="status"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Logging functions for both on-page and console output.
|
||||||
|
function log(msg, type = "info") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("status-line");
|
||||||
|
if (type === "error") div.classList.add("error");
|
||||||
|
if (type === "success") div.classList.add("success");
|
||||||
|
if (type === "warn") div.classList.add("warn");
|
||||||
|
div.textContent = msg;
|
||||||
|
document.getElementById("status").appendChild(div);
|
||||||
|
console.log(`[${type.toUpperCase()}] ${msg}`);
|
||||||
|
}
|
||||||
|
function clear() {
|
||||||
|
document.getElementById("status").innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.NostrTools) {
|
||||||
|
log("NostrTools not loaded. Check console or ad-blockers.", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructure the required functions and classes from nostr-tools.
|
||||||
|
// finalizeEvent is used to automatically compute the event id, sign the event, and assign the pubkey.
|
||||||
|
const {
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
finalizeEvent,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
SimplePool,
|
||||||
|
utils,
|
||||||
|
Relay, // Relay API for subscriptions.
|
||||||
|
} = window.NostrTools;
|
||||||
|
|
||||||
|
// Define relay URLs.
|
||||||
|
const RELAYS = [
|
||||||
|
"wss://relay.snort.social",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.primal.net",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create a SimplePool instance for publishing events.
|
||||||
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
// Main form submission handler.
|
||||||
|
document
|
||||||
|
.getElementById("dm-form")
|
||||||
|
.addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1) Retrieve and validate user input.
|
||||||
|
const npub = document.getElementById("npubInput").value.trim();
|
||||||
|
if (!npub.startsWith("npub")) {
|
||||||
|
throw new Error("Target must start with npub.");
|
||||||
|
}
|
||||||
|
const message =
|
||||||
|
document.getElementById("msgInput").value.trim() ||
|
||||||
|
"Hello from ephemeral DM!";
|
||||||
|
|
||||||
|
// 2) Decode the npub to obtain the target public key.
|
||||||
|
log("Decoding target npub...");
|
||||||
|
const decoded = nip19.decode(npub);
|
||||||
|
log(`[DEBUG] Decoded npub: ${JSON.stringify(decoded)}`);
|
||||||
|
if (decoded.type !== "npub") {
|
||||||
|
throw new Error("Decoded type is not npub.");
|
||||||
|
}
|
||||||
|
const targetPubHex = decoded.data;
|
||||||
|
log(`Target pubkey: ${targetPubHex.slice(0, 16)}...`);
|
||||||
|
|
||||||
|
// 3) Generate an ephemeral key pair.
|
||||||
|
log("Generating ephemeral key...");
|
||||||
|
const ephemeralPriv = generateSecretKey(); // Returns a Uint8Array.
|
||||||
|
const ephemeralPubHex = getPublicKey(ephemeralPriv); // Returns a hex string.
|
||||||
|
log(`Ephemeral pubkey: ${ephemeralPubHex.slice(0, 16)}...`);
|
||||||
|
|
||||||
|
// 4) Encrypt the message using NIP‑04 encryption.
|
||||||
|
log("Encrypting message (nip04)...");
|
||||||
|
const ciphertext = await nip04.encrypt(
|
||||||
|
ephemeralPriv,
|
||||||
|
targetPubHex,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
log(`[DEBUG] Ciphertext: ${ciphertext}`);
|
||||||
|
log("Encryption done.");
|
||||||
|
|
||||||
|
// 5) Build the DM event template (without id and signature).
|
||||||
|
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
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6) Finalize the event: compute the event id, sign it, and assign the pubkey.
|
||||||
|
const event = finalizeEvent(eventTemplate, ephemeralPriv);
|
||||||
|
log(`[DEBUG] Final event: ${JSON.stringify(event)}`);
|
||||||
|
|
||||||
|
// 7) Publish the event to all relays.
|
||||||
|
log("Publishing to relays...");
|
||||||
|
// pool.publish now accepts an array of relay URLs and returns a promise.
|
||||||
|
await Promise.any(pool.publish(RELAYS, event));
|
||||||
|
log("At least one relay accepted the event.", "success");
|
||||||
|
|
||||||
|
// 8) For each relay, connect using the Relay API and 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 DM in storage! ID: ${foundEvent.id.slice(
|
||||||
|
0,
|
||||||
|
8
|
||||||
|
)}...`,
|
||||||
|
"success"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEose() {
|
||||||
|
relay.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log(
|
||||||
|
"Done. If the logs show 'Relay accepted' and 'Found our DM in storage', the event is on at least one relay. Another client must subscribe to ephemeralPubHex or #p=targetPubHex to see it."
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
log("Error: " + err.message, "error");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user