Add files via upload

This commit is contained in:
Michilis
2025-02-09 22:17:46 +01:00
committed by GitHub
parent 956d902e9a
commit aa8d52e5e4
8 changed files with 79 additions and 32 deletions

View File

@@ -1,8 +1,21 @@
import React from 'react'; import React from 'react';
import { Link, Outlet } from 'react-router-dom'; import { Link, Outlet, useSearchParams } from 'react-router-dom';
import { Navigation } from './Navigation'; import { Navigation } from './Navigation';
export function Layout() { export function Layout() {
const [searchParams] = useSearchParams();
const isIframe = searchParams.get('iframe') === '1';
if (isIframe) {
return (
<div className="min-h-screen bg-gray-900 text-white">
<main className="container mx-auto px-4 py-4 md:py-8">
<Outlet />
</main>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gray-900 text-white"> <div className="min-h-screen bg-gray-900 text-white">
<Navigation /> <Navigation />

View File

@@ -1,18 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Link, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { Flame, Menu, X } from 'lucide-react'; import { Flame, Menu, X } from 'lucide-react';
import { useStore } from '../store/useStore'; import { useStore } from '../store/useStore';
export function Navigation() { export function Navigation() {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { user, setUser } = useStore(); const { user, setUser } = useStore();
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const isIframe = searchParams.get('iframe') === '1';
const handleLogout = () => { const handleLogout = () => {
setUser(null); setUser(null);
setIsMenuOpen(false); setIsMenuOpen(false);
navigate('/login', { replace: true }); // Use replace to prevent going back to dashboard const newPath = '/login' + (isIframe ? '?iframe=1' : '');
navigate(newPath, { replace: true });
}; };
const handleMenuClick = () => { const handleMenuClick = () => {
@@ -23,11 +26,16 @@ export function Navigation() {
setIsMenuOpen(false); setIsMenuOpen(false);
}; };
// Helper function to add iframe parameter to paths
const getPath = (path: string) => {
return isIframe ? `${path}?iframe=1` : path;
};
return ( return (
<nav className="border-b border-gray-800 bg-gray-900 sticky top-0 z-50"> <nav className="border-b border-gray-800 bg-gray-900 sticky top-0 z-50">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16"> <div className="flex items-center justify-between h-16">
<Link to="/" className="flex items-center space-x-2" onClick={handleNavigation}> <Link to={getPath('/')} className="flex items-center space-x-2" onClick={handleNavigation}>
{import.meta.env.VITE_LOGO_URL ? ( {import.meta.env.VITE_LOGO_URL ? (
<img <img
src={import.meta.env.VITE_LOGO_URL} src={import.meta.env.VITE_LOGO_URL}
@@ -47,7 +55,7 @@ export function Navigation() {
{user ? ( {user ? (
<> <>
<Link <Link
to="/dashboard" to={getPath('/dashboard')}
className="px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors" className="px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors"
> >
Dashboard Dashboard
@@ -61,7 +69,7 @@ export function Navigation() {
</> </>
) : ( ) : (
<Link <Link
to="/login" to={getPath('/login')}
className="px-4 py-2 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors" className="px-4 py-2 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors"
> >
Connect Nostr Connect Nostr
@@ -89,7 +97,7 @@ export function Navigation() {
{user ? ( {user ? (
<> <>
<Link <Link
to="/dashboard" to={getPath('/dashboard')}
onClick={handleNavigation} onClick={handleNavigation}
className="px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors" className="px-4 py-2 rounded-lg hover:bg-gray-800 transition-colors"
> >
@@ -104,7 +112,7 @@ export function Navigation() {
</> </>
) : ( ) : (
<Link <Link
to="/login" to={getPath('/login')}
onClick={handleNavigation} onClick={handleNavigation}
className="px-4 py-2 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors text-center" className="px-4 py-2 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors text-center"
> >

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { Shield, AlertTriangle, Zap, Copy, Activity, Users } from 'lucide-react'; import { Shield, AlertTriangle, Zap, Copy, Activity, Users, LogOut } from 'lucide-react';
import { useStore } from '../store/useStore'; import { useStore } from '../store/useStore';
import { apiService } from '../services/api'; import { apiService } from '../services/api';
import { Notification } from '../components/Notification'; import { Notification } from '../components/Notification';
@@ -15,6 +15,8 @@ export function Dashboard() {
const isDemoMode = import.meta.env.VITE_ENABLE_DEMO === 'true'; const isDemoMode = import.meta.env.VITE_ENABLE_DEMO === 'true';
const apiUrl = import.meta.env.VITE_API_URL; const apiUrl = import.meta.env.VITE_API_URL;
const { isVisible, message, type, showNotification, hideNotification } = useNotification(); const { isVisible, message, type, showNotification, hideNotification } = useNotification();
const [searchParams] = useSearchParams();
const isIframe = searchParams.get('iframe') === '1';
// Check user authentication and fetch status once // Check user authentication and fetch status once
useEffect(() => { useEffect(() => {
@@ -34,7 +36,6 @@ export function Dashboard() {
npub: userInfo.npub, npub: userInfo.npub,
}); });
} catch (error: any) { } catch (error: any) {
// If user is not found (404) or any other error, assume not whitelisted
if (error.response?.status === 404 || error) { if (error.response?.status === 404 || error) {
setUser({ setUser({
...user, ...user,
@@ -48,7 +49,7 @@ export function Dashboard() {
}; };
checkUserStatus(); checkUserStatus();
}, [user?.pubkey, navigate, setUser, isDemoMode]); // Only run on mount and when these dependencies change }, [user?.pubkey, navigate, setUser, isDemoMode]);
// Fetch uptime and active users // Fetch uptime and active users
useEffect(() => { useEffect(() => {
@@ -109,6 +110,11 @@ export function Dashboard() {
} }
}; };
const handleLogout = () => {
setUser(null);
navigate('/login');
};
if (loading || !user) { if (loading || !user) {
return ( return (
<div className="flex justify-center items-center min-h-[400px]"> <div className="flex justify-center items-center min-h-[400px]">
@@ -223,6 +229,19 @@ export function Dashboard() {
<p className="text-gray-400">Active Users</p> <p className="text-gray-400">Active Users</p>
</div> </div>
</div> </div>
{/* Logout Button (only shown in iframe mode) */}
{isIframe && (
<div className="flex justify-center">
<button
onClick={handleLogout}
className="flex items-center space-x-2 px-6 py-3 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
>
<LogOut className="h-5 w-5" />
<span>Logout</span>
</button>
</div>
)}
</div> </div>
<Notification <Notification
isVisible={isVisible} isVisible={isVisible}

View File

@@ -1,11 +1,13 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { Flame, Zap, Shield, Globe, Server, Code, Cpu, Copy } from 'lucide-react'; import { Flame, Zap, Shield, Globe, Server, Code, Cpu, Copy } from 'lucide-react';
import { Notification } from '../components/Notification'; import { Notification } from '../components/Notification';
import { useNotification } from '../hooks/useNotification'; import { useNotification } from '../hooks/useNotification';
export function Home() { export function Home() {
const { isVisible, message, type, showNotification, hideNotification } = useNotification(); const { isVisible, message, type, showNotification, hideNotification } = useNotification();
const [searchParams] = useSearchParams();
const isIframe = searchParams.get('iframe') === '1';
const copyToClipboard = async (text: string) => { const copyToClipboard = async (text: string) => {
try { try {
@@ -26,7 +28,7 @@ export function Home() {
A high-performance Nostr relay built by Bitcoiners, for Bitcoiners A high-performance Nostr relay built by Bitcoiners, for Bitcoiners
</p> </p>
<Link <Link
to="/login" to={isIframe ? '/login?iframe=1' : '/login'}
className="inline-block px-8 py-4 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors font-semibold text-lg" className="inline-block px-8 py-4 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors font-semibold text-lg"
> >
Get Started Get Started

View File

@@ -1,19 +1,21 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { Loader2, Zap } from 'lucide-react'; import { Loader2, Zap } from 'lucide-react';
import { useStore } from '../store/useStore'; import { useStore } from '../store/useStore';
export function Login() { export function Login() {
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { user, setUser } = useStore(); const { user, setUser } = useStore();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [pubkeyInput, setPubkeyInput] = useState(''); const [pubkeyInput, setPubkeyInput] = useState('');
const isIframe = searchParams.get('iframe') === '1';
useEffect(() => { useEffect(() => {
if (user) { if (user) {
navigate('/dashboard'); navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
} }
}, [user, navigate]); }, [user, navigate, isIframe]);
const handleExtensionLogin = async () => { const handleExtensionLogin = async () => {
setIsLoading(true); setIsLoading(true);
@@ -33,9 +35,8 @@ export function Login() {
throw new Error('No public key found'); throw new Error('No public key found');
} }
// Simply store the pubkey and navigate to dashboard
setUser({ pubkey, isWhitelisted: false }); setUser({ pubkey, isWhitelisted: false });
navigate('/dashboard'); navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
} catch (error: any) { } catch (error: any) {
if (error.message === 'Rejected by user') { if (error.message === 'Rejected by user') {
return; return;
@@ -62,9 +63,8 @@ export function Login() {
setIsLoading(true); setIsLoading(true);
try { try {
// Simply store the pubkey and navigate to dashboard
setUser({ pubkey: pubkeyInput.trim(), isWhitelisted: false }); setUser({ pubkey: pubkeyInput.trim(), isWhitelisted: false });
navigate('/dashboard'); navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error);
alert('Failed to login with the provided public key. Please check the format and try again.'); alert('Failed to login with the provided public key. Please check the format and try again.');

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { QRCodeSVG } from 'qrcode.react'; import { QRCodeSVG } from 'qrcode.react';
import { Copy, CheckCircle } from 'lucide-react'; import { Copy, CheckCircle } from 'lucide-react';
import { useStore } from '../store/useStore'; import { useStore } from '../store/useStore';
@@ -11,6 +11,7 @@ import { useNotification } from '../hooks/useNotification';
export function Payment() { export function Payment() {
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { user, setUser } = useStore(); const { user, setUser } = useStore();
const [invoice, setInvoice] = useState<LightningInvoice | null>(null); const [invoice, setInvoice] = useState<LightningInvoice | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -19,15 +20,16 @@ export function Payment() {
const { isVisible, message, type, showNotification, hideNotification } = useNotification(); const { isVisible, message, type, showNotification, hideNotification } = useNotification();
const [checkingPayment, setCheckingPayment] = useState(false); const [checkingPayment, setCheckingPayment] = useState(false);
const [showSuccess, setShowSuccess] = useState(false); const [showSuccess, setShowSuccess] = useState(false);
const isIframe = searchParams.get('iframe') === '1';
useEffect(() => { useEffect(() => {
if (!user) { if (!user) {
navigate('/login'); navigate(isIframe ? '/login?iframe=1' : '/login');
return; return;
} }
if (user.isWhitelisted) { if (user.isWhitelisted) {
navigate('/dashboard'); navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
return; return;
} }
@@ -59,12 +61,12 @@ export function Payment() {
}; };
generateInvoice(); generateInvoice();
}, [user, navigate]); }, [user, navigate, isIframe]);
const handlePaymentSuccess = async () => { const handlePaymentSuccess = async () => {
setShowSuccess(true); setShowSuccess(true);
setTimeout(() => { setTimeout(() => {
navigate('/thank-you'); navigate(isIframe ? '/thank-you?iframe=1' : '/thank-you');
}, 1500); }, 1500);
return true; return true;
}; };
@@ -147,7 +149,6 @@ export function Payment() {
return ( return (
<> <>
<div className="max-w-md mx-auto bg-gray-800 rounded-lg p-8 relative"> <div className="max-w-md mx-auto bg-gray-800 rounded-lg p-8 relative">
{/* Success Animation Overlay */}
{showSuccess && ( {showSuccess && (
<div className="absolute inset-0 bg-gray-900/95 flex items-center justify-center rounded-lg animate-fade-in z-10"> <div className="absolute inset-0 bg-gray-900/95 flex items-center justify-center rounded-lg animate-fade-in z-10">
<div className="text-center animate-success-appear"> <div className="text-center animate-success-appear">

View File

@@ -1,10 +1,13 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Shield } from 'lucide-react'; import { Shield } from 'lucide-react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
export function Terms() { export function Terms() {
const [terms, setTerms] = useState<string>(''); const [terms, setTerms] = useState<string>('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [searchParams] = useSearchParams();
const isIframe = searchParams.get('iframe') === '1';
useEffect(() => { useEffect(() => {
const fetchTerms = async () => { const fetchTerms = async () => {

View File

@@ -1,5 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { CheckCircle, Copy, ArrowRight } from 'lucide-react'; import { CheckCircle, Copy, ArrowRight } from 'lucide-react';
import { useStore } from '../store/useStore'; import { useStore } from '../store/useStore';
import confetti from 'canvas-confetti'; import confetti from 'canvas-confetti';
@@ -8,13 +8,15 @@ import { useNotification } from '../hooks/useNotification';
export function ThankYou() { export function ThankYou() {
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { user } = useStore(); const { user } = useStore();
const { isVisible, message, type, showNotification, hideNotification } = useNotification(); const { isVisible, message, type, showNotification, hideNotification } = useNotification();
const relayUrl = import.meta.env.VITE_NOSTR_RELAY_URL; const relayUrl = import.meta.env.VITE_NOSTR_RELAY_URL;
const isIframe = searchParams.get('iframe') === '1';
useEffect(() => { useEffect(() => {
if (!user) { if (!user) {
navigate('/login'); navigate(isIframe ? '/login?iframe=1' : '/login');
return; return;
} }
@@ -36,7 +38,6 @@ export function ThankYou() {
const particleCount = 50 * (timeLeft / duration); const particleCount = 50 * (timeLeft / duration);
// Since they fall down, start a bit higher than random
confetti({ confetti({
...defaults, ...defaults,
particleCount, particleCount,
@@ -50,7 +51,7 @@ export function ThankYou() {
}, 250); }, 250);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [user, navigate]); }, [user, navigate, isIframe]);
const copyToClipboard = async (text: string) => { const copyToClipboard = async (text: string) => {
try { try {
@@ -114,7 +115,7 @@ export function ThankYou() {
</div> </div>
<button <button
onClick={() => navigate('/dashboard')} onClick={() => navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard')}
className="px-8 py-4 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors font-semibold" className="px-8 py-4 bg-orange-500 rounded-lg hover:bg-orange-600 transition-colors font-semibold"
> >
Go to Dashboard Go to Dashboard