390 lines
11 KiB
Plaintext
390 lines
11 KiB
Plaintext
package views
|
|
|
|
import (
|
|
"nebula/components"
|
|
"nebula/layouts"
|
|
)
|
|
|
|
var welcomeSteps = []string{"Welcome", "Learn", "Get Started"}
|
|
|
|
// WelcomePage renders the full onboarding page with the specified step
|
|
templ WelcomePage(currentStep int) {
|
|
@layouts.CenteredCard("Welcome") {
|
|
<div slot="header" id="stepper-container" hx-swap-oob:inherited="true">
|
|
@components.OnboardingStepper(currentStep, welcomeSteps)
|
|
</div>
|
|
<div id="step-content" class="step-content">
|
|
@WelcomeStepContent(currentStep)
|
|
</div>
|
|
<div id="htmx-indicator" class="htmx-indicator">
|
|
<wa-spinner></wa-spinner>
|
|
</div>
|
|
<footer slot="footer">
|
|
@WelcomeFooter()
|
|
</footer>
|
|
}
|
|
}
|
|
|
|
// WelcomeStepContent renders the content for a specific step (used for HTMX partials)
|
|
templ WelcomeStepContent(step int) {
|
|
switch step {
|
|
case 1:
|
|
@WelcomeStep1()
|
|
case 2:
|
|
@WelcomeStep2()
|
|
case 3:
|
|
@WelcomeStep3()
|
|
}
|
|
}
|
|
|
|
// WelcomeStepWithStepper renders step content with OOB stepper update for HTMX 4
|
|
templ WelcomeStepWithStepper(step int) {
|
|
@WelcomeStepContent(step)
|
|
<div id="stepper-container" hx-swap-oob="innerHTML">
|
|
@components.OnboardingStepper(step, welcomeSteps)
|
|
</div>
|
|
}
|
|
|
|
// WelcomeStep1 - Initial welcome screen with features
|
|
templ WelcomeStep1() {
|
|
@welcomeStyles()
|
|
<div class="wa-stack wa-gap-l">
|
|
<div class="hero-icon">
|
|
<wa-icon name="wallet" family="duotone"></wa-icon>
|
|
</div>
|
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
|
<h1 class="wa-heading-xl">Welcome to Sonr</h1>
|
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
|
Your self-sovereign identity wallet powered by WebAssembly
|
|
</p>
|
|
</div>
|
|
<wa-divider></wa-divider>
|
|
<div class="wa-stack wa-gap-s">
|
|
@FeatureItem("shield-check", "security", "Passwordless Security", "Authenticate with biometrics or hardware keys - no passwords to remember or steal")
|
|
@FeatureItem("bolt", "speed", "WASM-Powered", "Wallet runs entirely in your browser - fast, secure, and always available")
|
|
@FeatureItem("user-shield", "privacy", "You Own Your Data", "Self-sovereign identity means your keys never leave your device")
|
|
</div>
|
|
<wa-button
|
|
variant="brand"
|
|
size="large"
|
|
style="width: 100%;"
|
|
hx-get="/welcome/step/2"
|
|
hx-target="#step-content"
|
|
hx-swap="innerHTML transition:true"
|
|
hx-indicator="#htmx-indicator"
|
|
hx-on:htmx:after:swap="this.closest('.step-content')?.scrollTo(0,0)"
|
|
>
|
|
Learn More
|
|
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
|
</wa-button>
|
|
</div>
|
|
}
|
|
|
|
// WelcomeStep2 - How Motr Works explanation
|
|
templ WelcomeStep2() {
|
|
@welcomeStyles()
|
|
<div class="wa-stack wa-gap-l">
|
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
|
<h2 class="wa-heading-l">How Motr Works</h2>
|
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
|
A next-generation wallet built on WebAuthn and WASM
|
|
</p>
|
|
</div>
|
|
<wa-divider></wa-divider>
|
|
<div class="wa-stack wa-gap-m">
|
|
@InfoCallout("microchip", "WebAssembly Runtime", "Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.")
|
|
@InfoCallout("fingerprint", "Passkey Authentication", "Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.")
|
|
@InfoCallout("network-wired", "Decentralized Identity", "Connect to any app with OpenID Connect - you control what data to share.")
|
|
@InfoCallout("key", "Multi-Chain Support", "One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.")
|
|
</div>
|
|
<div class="wa-cluster wa-gap-s" style="justify-content: space-between;">
|
|
<wa-button
|
|
variant="neutral"
|
|
appearance="outlined"
|
|
hx-get="/welcome/step/1"
|
|
hx-target="#step-content"
|
|
hx-swap="innerHTML transition:true"
|
|
hx-indicator="#htmx-indicator"
|
|
>
|
|
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
|
Back
|
|
</wa-button>
|
|
<wa-button
|
|
variant="brand"
|
|
hx-get="/welcome/step/3"
|
|
hx-target="#step-content"
|
|
hx-swap="innerHTML transition:true"
|
|
hx-indicator="#htmx-indicator"
|
|
>
|
|
Get Started
|
|
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
|
</wa-button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// WelcomeStep3 - Action selection (Create/Sign In)
|
|
templ WelcomeStep3() {
|
|
@welcomeStyles()
|
|
<div class="wa-stack wa-gap-l">
|
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
|
<h2 class="wa-heading-l">Ready to Begin?</h2>
|
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
|
Create a new wallet or sign in to an existing one
|
|
</p>
|
|
</div>
|
|
<wa-divider></wa-divider>
|
|
<div class="wa-grid" style="--min-column-size: 180px; gap: var(--wa-space-m);">
|
|
<a href="/register" class="action-card">
|
|
<wa-icon name="user-plus" family="duotone"></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Create Wallet</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
|
New to Sonr? Set up your wallet in minutes
|
|
</span>
|
|
</div>
|
|
</a>
|
|
<a href="/login" class="action-card">
|
|
<wa-icon name="right-to-bracket" family="duotone"></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Sign In</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
|
Access your existing wallet
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
<wa-divider>or</wa-divider>
|
|
<wa-button
|
|
variant="neutral"
|
|
appearance="outlined"
|
|
style="width: 100%;"
|
|
hx-get="/welcome/qr-scanner"
|
|
hx-target="#step-content"
|
|
hx-swap="innerHTML transition:true"
|
|
hx-indicator="#htmx-indicator"
|
|
>
|
|
<wa-icon slot="start" name="qrcode"></wa-icon>
|
|
Scan QR Code
|
|
</wa-button>
|
|
<wa-callout variant="brand" appearance="filled">
|
|
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
|
<span class="wa-caption-s">
|
|
Already have a passkey registered on another device? Use QR code to sync your wallet.
|
|
</span>
|
|
</wa-callout>
|
|
<wa-button
|
|
variant="neutral"
|
|
appearance="plain"
|
|
style="width: 100%;"
|
|
hx-get="/welcome/step/2"
|
|
hx-target="#step-content"
|
|
hx-swap="innerHTML transition:true"
|
|
hx-indicator="#htmx-indicator"
|
|
>
|
|
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
|
Back to How It Works
|
|
</wa-button>
|
|
</div>
|
|
}
|
|
|
|
// WelcomeFooter renders the network status and links footer
|
|
templ WelcomeFooter() {
|
|
@welcomeStyles()
|
|
<div class="wa-stack wa-gap-m wa-align-items-center">
|
|
<div class="network-status">
|
|
<div class="status-dot"></div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
|
Sonr Network: <strong style="color: var(--wa-color-success);">Operational</strong>
|
|
</span>
|
|
</div>
|
|
<div class="wa-cluster wa-justify-content-center wa-gap-m">
|
|
<wa-button appearance="plain" size="small" onclick="window.open('https://sonr.io', '_blank')">
|
|
Learn about Sonr
|
|
</wa-button>
|
|
<span style="color: var(--wa-color-neutral-300);">|</span>
|
|
<wa-button appearance="plain" size="small" onclick="window.open('https://docs.sonr.io', '_blank')">
|
|
Documentation
|
|
</wa-button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// FeatureItem renders a feature highlight with icon
|
|
templ FeatureItem(icon string, variant string, title string, description string) {
|
|
<div class="feature-item">
|
|
<div class={ "feature-icon", variant }>
|
|
<wa-icon name={ icon }></wa-icon>
|
|
</div>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xs">{ title }</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
|
{ description }
|
|
</span>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// InfoCallout renders an informational callout box
|
|
templ InfoCallout(icon string, title string, description string) {
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name={ icon }></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xs">{ title }</span>
|
|
<span class="wa-caption-s">{ description }</span>
|
|
</div>
|
|
</wa-callout>
|
|
}
|
|
|
|
// welcomeStyles contains the CSS specific to the welcome page
|
|
templ welcomeStyles() {
|
|
<style>
|
|
/* HTMX 4 indicator and transition styles */
|
|
.htmx-indicator {
|
|
display: none;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 100;
|
|
}
|
|
.htmx-request .htmx-indicator {
|
|
display: flex;
|
|
}
|
|
.htmx-request.step-content {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
/* View transition support for HTMX 4 */
|
|
@view-transition {
|
|
navigation: auto;
|
|
}
|
|
::view-transition-old(step-content),
|
|
::view-transition-new(step-content) {
|
|
animation-duration: 0.25s;
|
|
}
|
|
.step-content {
|
|
view-transition-name: step-content;
|
|
position: relative;
|
|
}
|
|
.hero-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: var(--wa-radius-l);
|
|
background: linear-gradient(135deg, var(--wa-color-primary), #0090ff);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto var(--wa-space-l);
|
|
box-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);
|
|
}
|
|
.hero-icon wa-icon {
|
|
font-size: 40px;
|
|
color: white;
|
|
}
|
|
.feature-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
.feature-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: var(--wa-radius-s);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.feature-icon.security {
|
|
background: var(--wa-color-success-subtle);
|
|
color: var(--wa-color-success);
|
|
}
|
|
.feature-icon.speed {
|
|
background: var(--wa-color-primary-subtle);
|
|
color: var(--wa-color-primary);
|
|
}
|
|
.feature-icon.privacy {
|
|
background: var(--wa-color-warning-subtle);
|
|
color: var(--wa-color-warning);
|
|
}
|
|
.action-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-xl);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-l);
|
|
border: 2px solid transparent;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
.action-card:hover {
|
|
border-color: var(--wa-color-primary);
|
|
background: var(--wa-color-primary-subtle);
|
|
}
|
|
.action-card wa-icon {
|
|
font-size: 2.5rem;
|
|
color: var(--wa-color-primary);
|
|
}
|
|
.network-status {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--wa-space-xs);
|
|
}
|
|
.status-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--wa-color-success);
|
|
animation: pulse 2s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
/* Viewport constraints for popup/webview */
|
|
@media (max-width: 400px) {
|
|
.hero-icon {
|
|
width: 64px;
|
|
height: 64px;
|
|
}
|
|
.hero-icon wa-icon {
|
|
font-size: 32px;
|
|
}
|
|
.feature-item {
|
|
padding: var(--wa-space-s);
|
|
}
|
|
.feature-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
.action-card {
|
|
padding: var(--wa-space-m);
|
|
}
|
|
.action-card wa-icon {
|
|
font-size: 2rem;
|
|
}
|
|
}
|
|
@media (max-height: 600px) {
|
|
.hero-icon {
|
|
width: 56px;
|
|
height: 56px;
|
|
margin-bottom: var(--wa-space-m);
|
|
}
|
|
.hero-icon wa-icon {
|
|
font-size: 28px;
|
|
}
|
|
}
|
|
</style>
|
|
}
|