added better markdown view pages and enabled subscription button to know when your logged in

This commit is contained in:
Keep Creating Online
2025-02-06 15:15:29 -05:00
parent 6cf1d53624
commit 7e661ba0fd
24 changed files with 487 additions and 3240 deletions

View File

@@ -110,6 +110,9 @@ class bitvidApp {
this.copyMagnetBtn = null;
this.shareBtn = null;
// Hide/Show Subscriptions Link
this.subscriptionsLink = null;
// Notification containers
this.errorContainer = document.getElementById("errorContainer") || null;
this.successContainer = document.getElementById("successContainer") || null;
@@ -185,10 +188,19 @@ class bitvidApp {
// 4. Connect to Nostr
await nostrClient.init();
// Grab the "Subscriptions" link by its id in the sidebar
this.subscriptionsLink = document.getElementById("subscriptionsLink");
const savedPubKey = localStorage.getItem("userPubKey");
if (savedPubKey) {
// Auto-login if a pubkey was saved
this.login(savedPubKey, false);
// If the user was already logged in, show the Subscriptions link
if (this.subscriptionsLink) {
this.subscriptionsLink.classList.remove("hidden");
}
}
// 5. Setup general event listeners, show disclaimers
@@ -726,6 +738,11 @@ class bitvidApp {
this.profileButton.classList.remove("hidden");
}
// Show the "Subscriptions" link if it exists
if (this.subscriptionsLink) {
this.subscriptionsLink.classList.remove("hidden");
}
// (Optional) load the user's own Nostr profile
this.loadOwnProfile(pubkey);
@@ -772,6 +789,11 @@ class bitvidApp {
this.profileButton.classList.add("hidden");
}
// Hide the Subscriptions link
if (this.subscriptionsLink) {
this.subscriptionsLink.classList.add("hidden");
}
// Clear localStorage
localStorage.removeItem("userPubKey");
@@ -810,31 +832,31 @@ class bitvidApp {
* Hide the video modal.
*/
async hideModal() {
// 1) Clear intervals
// 1) Clear intervals, cleanup, etc. (unchanged)
if (this.activeIntervals && this.activeIntervals.length) {
this.activeIntervals.forEach((id) => clearInterval(id));
this.activeIntervals = [];
}
// *** EXPLICITLY CANCEL THE SERVICE WORKER STREAM ***
try {
await fetch("/webtorrent/cancel/", { mode: "no-cors" });
} catch (err) {
// Silently ignore if offline or the request fails
// ignore
}
// 2) Cleanup resources (stops WebTorrent, etc.)
await this.cleanup();
// 3) Hide the modal
// 2) Hide the modal
if (this.playerModal) {
this.playerModal.style.display = "none";
this.playerModal.classList.add("hidden");
}
this.currentMagnetUri = null;
// 4) Revert ?v= param in the URL
window.history.replaceState({}, "", window.location.pathname);
// 3) Remove only `?v=` but **keep** the hash
const url = new URL(window.location.href);
url.searchParams.delete("v"); // remove ?v= param
const newUrl = url.pathname + url.search + url.hash;
window.history.replaceState({}, "", newUrl);
}
/**

View File

@@ -79,6 +79,8 @@ Promise.all([
});
}
// Load and set up sidebar navigation
// (We assume it calls setHashView(...) now)
return import("./sidebar.js").then((module) => {
module.setupSidebarNavigation();
});
@@ -145,46 +147,76 @@ Promise.all([
});
}
// 5) ?modal=appeals => open content appeals form
const urlParams = new URLSearchParams(window.location.search);
const modalParam = urlParams.get("modal");
// Once everything is loaded, handle the query params (modal? v?) & disclaimers
handleQueryParams();
if (modalParam === "appeals") {
const appealsModal = document.getElementById("contentAppealsModal");
if (appealsModal) {
appealsModal.classList.remove("hidden");
}
const closeAppealsBtn = document.getElementById(
"closeContentAppealsModal"
);
if (closeAppealsBtn) {
closeAppealsBtn.addEventListener("click", () => {
appealsModal.classList.add("hidden");
if (!localStorage.getItem("hasSeenDisclaimer")) {
const disclaimerModal = document.getElementById("disclaimerModal");
if (disclaimerModal) {
disclaimerModal.classList.remove("hidden");
}
}
});
}
} else if (modalParam === "application") {
const appModal = document.getElementById("nostrFormModal");
if (appModal) {
appModal.classList.remove("hidden");
}
} else {
// If there's no special param, disclaimers can show if user hasn't seen them
const hasSeenDisclaimer = localStorage.getItem("hasSeenDisclaimer");
if (!hasSeenDisclaimer) {
const disclaimerModal = document.getElementById("disclaimerModal");
if (disclaimerModal) {
disclaimerModal.classList.remove("hidden");
}
}
// Listen for hash changes
window.addEventListener("hashchange", handleHashChange);
// Also run once on initial load
handleHashChange();
});
/* -------------------------------------------
HELPER FUNCTIONS FOR QUERY AND HASH
-------------------------------------------- */
/**
* Sets the location.hash to "#view=<viewName>",
* removing any ?modal=... or ?v=... from the query string.
*/
export function setHashView(viewName) {
const url = new URL(window.location.href);
// Remove possibly conflicting query params
url.searchParams.delete("modal");
url.searchParams.delete("v");
// Keep the existing path + updated search, set the new hash
const newUrl = url.pathname + url.search + `#view=${viewName}`;
// Replace the URL so no full reload
window.history.replaceState({}, "", newUrl);
// Manually trigger handleHashChange so the view loads immediately
handleHashChange();
}
/**
* Sets a query param (e.g. ?modal=xxx or ?v=yyy),
* removing any "#view=..." from the hash to avoid collisions.
*/
export function setQueryParam(key, value) {
const url = new URL(window.location.href);
// Remove any #view=... from the hash
url.hash = "";
// Set the query param
url.searchParams.set(key, value);
// Replace the URL
const newUrl = url.pathname + url.search;
window.history.replaceState({}, "", newUrl);
// Immediately handle it
handleQueryParams();
}
/**
* Check the current URL for ?modal=..., ?v=..., etc.
* Open the correct modals or disclaimers as needed.
*/
function handleQueryParams() {
const urlParams = new URLSearchParams(window.location.search);
const modalParam = urlParams.get("modal");
// 5) Check ?modal=... param (moved from original code)
if (modalParam === "appeals") {
const appealsModal = document.getElementById("contentAppealsModal");
if (appealsModal) {
appealsModal.classList.remove("hidden");
}
// 6) Close content appeals modal if needed
const closeAppealsBtn = document.getElementById("closeContentAppealsModal");
if (closeAppealsBtn) {
closeAppealsBtn.addEventListener("click", () => {
@@ -192,67 +224,114 @@ Promise.all([
if (appealsModal) {
appealsModal.classList.add("hidden");
}
});
}
// 7) Disclaimer 'I Understand' Button
const acceptDisclaimerBtn = document.getElementById("acceptDisclaimer");
if (acceptDisclaimerBtn) {
acceptDisclaimerBtn.addEventListener("click", () => {
const disclaimerModal = document.getElementById("disclaimerModal");
if (disclaimerModal) {
disclaimerModal.classList.add("hidden");
// Show disclaimer if not seen
if (!localStorage.getItem("hasSeenDisclaimer")) {
const disclaimerModal = document.getElementById("disclaimerModal");
if (disclaimerModal) {
disclaimerModal.classList.remove("hidden");
}
}
localStorage.setItem("hasSeenDisclaimer", "true");
});
}
} else if (modalParam === "application") {
const appModal = document.getElementById("nostrFormModal");
if (appModal) {
appModal.classList.remove("hidden");
}
} else {
// If there's no special param, disclaimers can show if user hasn't seen them
const hasSeenDisclaimer = localStorage.getItem("hasSeenDisclaimer");
if (!hasSeenDisclaimer) {
const disclaimerModal = document.getElementById("disclaimerModal");
if (disclaimerModal) {
disclaimerModal.classList.remove("hidden");
}
}
}
// 8) Query param checks for the three new forms
if (modalParam === "feedback") {
// 8) Additional forms
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
const closeFeedbackBtn = document.getElementById("closeGeneralFeedbackModal");
if (closeFeedbackBtn) {
closeFeedbackBtn.addEventListener("click", () => {
const feedbackModal = document.getElementById("generalFeedbackModal");
if (feedbackModal) {
feedbackModal.classList.remove("hidden");
feedbackModal.classList.add("hidden");
}
} else if (modalParam === "feature") {
});
}
const closeFeatureBtn = document.getElementById("closeFeatureRequestModal");
if (closeFeatureBtn) {
closeFeatureBtn.addEventListener("click", () => {
const featureModal = document.getElementById("featureRequestModal");
if (featureModal) {
featureModal.classList.remove("hidden");
featureModal.classList.add("hidden");
}
} else if (modalParam === "bug") {
});
}
const closeBugBtn = document.getElementById("closeBugFixModal");
if (closeBugBtn) {
closeBugBtn.addEventListener("click", () => {
const bugModal = document.getElementById("bugFixModal");
if (bugModal) {
bugModal.classList.remove("hidden");
bugModal.classList.add("hidden");
}
}
});
}
// 9) Close buttons for the new forms
const closeFeedbackBtn = document.getElementById(
"closeGeneralFeedbackModal"
);
if (closeFeedbackBtn) {
closeFeedbackBtn.addEventListener("click", () => {
const feedbackModal = document.getElementById("generalFeedbackModal");
if (feedbackModal) {
feedbackModal.classList.add("hidden");
// You could also check ?v=someEvent to open a video, etc.
// if you want to keep ?v= param logic here.
// ...
}
/**
* Handle #view=... in the hash and load the correct partial view.
*/
function handleHashChange() {
const hash = window.location.hash || "";
// Expecting something like #view=most-recent-videos
const match = hash.match(/^#view=(.+)/);
// If no #view=..., load "most-recent-videos" as default
if (!match || !match[1]) {
import("./viewManager.js").then(({ loadView, viewInitRegistry }) => {
loadView("views/most-recent-videos.html").then(() => {
const initFn = viewInitRegistry["most-recent-videos"];
if (typeof initFn === "function") {
initFn();
}
});
}
const closeFeatureBtn = document.getElementById("closeFeatureRequestModal");
if (closeFeatureBtn) {
closeFeatureBtn.addEventListener("click", () => {
const featureModal = document.getElementById("featureRequestModal");
if (featureModal) {
featureModal.classList.add("hidden");
}
});
}
const closeBugBtn = document.getElementById("closeBugFixModal");
if (closeBugBtn) {
closeBugBtn.addEventListener("click", () => {
const bugModal = document.getElementById("bugFixModal");
if (bugModal) {
bugModal.classList.add("hidden");
}
});
}
});
return;
}
const viewName = match[1];
const viewUrl = `views/${viewName}.html`;
// Load the partial
import("./viewManager.js").then(({ loadView, viewInitRegistry }) => {
loadView(viewUrl).then(() => {
const initFn = viewInitRegistry[viewName];
if (typeof initFn === "function") {
initFn();
}
});
});
}

View File

@@ -1,31 +1,21 @@
//js/sidebar.js
import { loadView } from "./viewManager.js";
import { viewInitRegistry } from "./viewManager.js";
// js/sidebar.js
import { setHashView } from "./index.js"; // <--- or wherever you put it
export function setupSidebarNavigation() {
// Grab all primary nav links that use the "#view=..." pattern
const sidebarLinks = document.querySelectorAll('#sidebar a[href^="#view="]');
sidebarLinks.forEach((link) => {
link.addEventListener("click", (e) => {
e.preventDefault();
// For a link like "#view=most-recent-videos", parse out "most-recent-videos"
const href = link.getAttribute("href") || "";
const viewMatch = href.match(/#view=(.+)/);
if (!viewMatch || !viewMatch[1]) {
return;
}
const viewName = viewMatch[1]; // e.g. "most-recent-videos"
const viewUrl = `views/${viewName}.html`;
// Load the partial view
loadView(viewUrl).then(() => {
// If there's a post-load function for this view, call it
const initFn = viewInitRegistry[viewName];
if (typeof initFn === "function") {
initFn();
}
});
// e.g. "#view=about"
const href = link.getAttribute("href") || "";
const match = href.match(/^#view=(.+)/);
if (!match) return;
const viewName = match[1]; // "about", "ipns", etc.
setHashView(viewName);
// That removes ?modal=, ?v=, sets #view=viewName,
// and triggers handleHashChange() automatically
});
});
}

View File

@@ -1,14 +1,32 @@
// js/viewManager.js
// Load a partial view by URL into the #viewContainer
// js/viewManager.js
export async function loadView(viewUrl) {
try {
const res = await fetch(viewUrl);
if (!res.ok) {
throw new Error(`Failed to load view: ${res.status}`);
}
const html = await res.text();
document.getElementById("viewContainer").innerHTML = html;
const text = await res.text();
// DOMParser, parse out the body, inject
const parser = new DOMParser();
const doc = parser.parseFromString(text, "text/html");
const container = document.getElementById("viewContainer");
container.innerHTML = doc.body.innerHTML;
// Now copy and execute each script
const scriptTags = doc.querySelectorAll("script");
scriptTags.forEach((oldScript) => {
const newScript = document.createElement("script");
Array.from(oldScript.attributes).forEach((attr) => {
newScript.setAttribute(attr.name, attr.value);
});
newScript.textContent = oldScript.textContent;
container.appendChild(newScript);
});
} catch (err) {
console.error("View loading error:", err);
document.getElementById("viewContainer").innerHTML =