Files
bitvid/js/ui/onboarding/bitvidTour.js
T
thePR0M3TH3ANandClaude Opus 4.8 4591d4a260 [onboarding] Fix popover pushed off-screen by full-height targets (sidebar step)
Step 2 (sidebar) left the user "stuck looking at a glow with nothing to do": the
sidebar is a full-height column, so the below/above-only placement computed a
popover position past the bottom of the viewport — the card existed but was
invisible off-screen.

Placement is now a pure, exported decision (computeTourPlacement): try below →
above → beside-right → beside-left, whichever actually fits the popover, with a
centered-over-the-dim fallback when nothing does (viewport-filling targets) — and
coordinates are clamped so the card can NEVER leave the viewport. The sidebar
step also hints placement: "right". A preferred placement that doesn't fit falls
back to one that does.

Tests: +1 in tests/onboarding-tour.test.mjs — full-height column → beside-right
inside the viewport; button → bottom; near-bottom target → top; viewport-filling
target → centered; unfittable preference falls back and never goes below the
fold. Lint + build green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 22:50:46 -04:00

93 lines
3.0 KiB
JavaScript

// bitvid's first-run guided tour (docs/onboarding-plan.md): the step script fed
// into the generic tourEngine. Anchors use existing stable selectors (ids +
// data-testid added for e2e), and steps whose anchor is absent in the current
// layout are skipped automatically by the engine.
import { createTour } from "./tourEngine.js";
import {
shouldOfferOnboarding,
markOnboarding,
} from "../../services/onboardingService.js";
export function buildBitvidTourSteps({ openProfilePane } = {}) {
const open = (pane) => () => {
try {
openProfilePane?.(pane);
} catch (error) {
// best-effort deep link
}
};
return [
{
id: "welcome",
title: "Welcome to bitvid 👋",
body:
"bitvid is decentralized video on Nostr — your identity, follows, and videos belong to you, not a platform. Here's a quick tour (30 seconds).",
},
{
id: "feeds",
target: "#sidebar",
placement: "right", // full-height column — popover beside it, not below
title: "Your feeds",
body:
"Recent shows the newest videos across the network. For You learns from what you watch, and Trending ranks by views. Switch any time from this sidebar.",
},
{
id: "subscriptions",
target: "#subscriptionsLink",
title: "Follow creators",
body:
"Subscribe to channels you like and they'll fill this feed. Your follow list is a Nostr list — it travels with your account to any client.",
},
{
id: "upload",
target: '[data-testid="upload-button"]',
title: "Post your own videos",
body:
"Upload to your own storage (Cloudflare R2, S3, B2) or paste a link to a video hosted anywhere. bitvid adds WebTorrent peer-to-peer streaming on top automatically.",
},
{
id: "profile",
target: '[data-testid="profile-button"]',
title: "Everything else lives here",
body:
"Your profile menu holds relays, direct messages, storage, your wallet, moderation settings — and this tour, if you ever want it again.",
},
{
id: "finish",
title: "You're set 🎉",
body:
"Two optional power-ups: connect storage so you can upload videos, and connect a Lightning wallet (NWC) so you can zap creators.",
actions: [
{ label: "Set up storage", onClick: open("storage"), variant: "ghost" },
{ label: "Connect wallet", onClick: open("wallet"), variant: "ghost" },
],
},
];
}
// Launch the tour for a pubkey. `force` (the "Take the tour" button) ignores the
// once-per-account flag. Returns true when the tour actually started.
export function launchBitvidTour({
pubkey,
openProfilePane,
document: doc,
force = false,
} = {}) {
if (!force && !shouldOfferOnboarding(pubkey)) {
return false;
}
const tour = createTour({
steps: buildBitvidTourSteps({ openProfilePane }),
document: doc,
onFinish: () => markOnboarding(pubkey, "completed"),
onSkip: () => markOnboarding(pubkey, "skipped"),
});
return Boolean(tour?.start());
}
export default launchBitvidTour;