Fixing user refresh isuue

This commit is contained in:
Dario Ghunney Ware
2025-10-15 20:11:49 +01:00
parent 01d18279e0
commit a969f13715
22 changed files with 390 additions and 103 deletions

View File

@@ -1,5 +1,5 @@
import { Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Routes, Route } from "react-router-dom";
import { RainbowThemeProvider } from "./components/shared/RainbowThemeProvider";
import { FileContextProvider } from "./contexts/FileContext";
import { NavigationProvider } from "./contexts/NavigationContext";
@@ -52,8 +52,7 @@ const LoadingFallback = () => (
export default function App() {
return (
<Suspense fallback={<LoadingFallback />}>
<BrowserRouter>
<PreferencesProvider>
<PreferencesProvider>
<RainbowThemeProvider>
<ErrorBoundary>
<AuthProvider>
@@ -83,16 +82,16 @@ export default function App() {
<OnboardingTour />
</TourOrchestrationProvider>
</RightRailProvider>
</SignatureProvider>
</ViewerProvider>
</SidebarProvider>
</HotkeyProvider>
</ToolWorkflowProvider>
</FilesModalProvider>
</NavigationProvider>
</ToolRegistryProvider>
</FileContextProvider>
</OnboardingProvider>
</SignatureProvider>
</ViewerProvider>
</SidebarProvider>
</HotkeyProvider>
</ToolWorkflowProvider>
</FilesModalProvider>
</NavigationProvider>
</ToolRegistryProvider>
</FileContextProvider>
</OnboardingProvider>
}
/>
</Routes>
@@ -100,7 +99,6 @@ export default function App() {
</ErrorBoundary>
</RainbowThemeProvider>
</PreferencesProvider>
</BrowserRouter>
</Suspense>
);
}

View File

@@ -0,0 +1,159 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { BASE_PATH } from '../../constants/app';
type ImageSlide = { src: string; alt?: string; cornerModelUrl?: string; title?: string; subtitle?: string; followMouseTilt?: boolean; tiltMaxDeg?: number }
export default function LoginRightCarousel({
imageSlides = [],
showBackground = true,
initialSeconds = 5,
slideSeconds = 8,
}: {
imageSlides?: ImageSlide[]
showBackground?: boolean
initialSeconds?: number
slideSeconds?: number
}) {
const totalSlides = imageSlides.length
const [index, setIndex] = useState(0)
const mouse = useRef({ x: 0, y: 0 })
const durationsMs = useMemo(() => {
if (imageSlides.length === 0) return []
return imageSlides.map((_, i) => (i === 0 ? (initialSeconds ?? slideSeconds) : slideSeconds) * 1000)
}, [imageSlides, initialSeconds, slideSeconds])
useEffect(() => {
if (totalSlides <= 1) return
const timeout = setTimeout(() => {
setIndex((i) => (i + 1) % totalSlides)
}, durationsMs[index] ?? slideSeconds * 1000)
return () => clearTimeout(timeout)
}, [index, totalSlides, durationsMs, slideSeconds])
useEffect(() => {
const onMove = (e: MouseEvent) => {
mouse.current.x = (e.clientX / window.innerWidth) * 2 - 1
mouse.current.y = (e.clientY / window.innerHeight) * 2 - 1
}
window.addEventListener('mousemove', onMove)
return () => window.removeEventListener('mousemove', onMove)
}, [])
function TiltImage({ src, alt, enabled, maxDeg = 6 }: { src: string; alt?: string; enabled: boolean; maxDeg?: number }) {
const imgRef = useRef<HTMLImageElement | null>(null)
useEffect(() => {
const el = imgRef.current
if (!el) return
let raf = 0
const tick = () => {
if (enabled) {
const rotY = (mouse.current.x || 0) * maxDeg
const rotX = -(mouse.current.y || 0) * maxDeg
el.style.transform = `translateY(-2rem) rotateX(${rotX.toFixed(2)}deg) rotateY(${rotY.toFixed(2)}deg)`
} else {
el.style.transform = 'translateY(-2rem)'
}
raf = requestAnimationFrame(tick)
}
raf = requestAnimationFrame(tick)
return () => cancelAnimationFrame(raf)
}, [enabled, maxDeg])
return (
<img
ref={imgRef}
src={src}
alt={alt ?? 'Carousel slide'}
style={{
maxWidth: '86%',
maxHeight: '78%',
objectFit: 'contain',
borderRadius: '18px',
background: 'transparent',
transform: 'translateY(-2rem)',
transition: 'transform 80ms ease-out',
willChange: 'transform',
transformOrigin: '50% 50%',
}}
/>
)
}
return (
<div style={{ position: 'relative', overflow: 'hidden', width: '100%', height: '100%' }}>
{showBackground && (
<img
src={`${BASE_PATH}/Login/LoginBackgroundPanel.png`}
alt="Background panel"
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
/>
)}
{/* Image slides */}
{imageSlides.map((s, idx) => (
<div
key={s.src}
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'opacity 600ms ease',
opacity: index === idx ? 1 : 0,
perspective: '900px',
}}
>
{(s.title || s.subtitle) && (
<div style={{ position: 'absolute', bottom: 24 + 32, left: 0, right: 0, textAlign: 'center', padding: '0 2rem', width: '100%' }}>
{s.title && (
<div style={{ fontSize: 20, fontWeight: 800, color: '#ffffff', textShadow: '0 2px 6px rgba(0,0,0,0.25)', marginBottom: 6 }}>{s.title}</div>
)}
{s.subtitle && (
<div style={{ fontSize: 13, color: 'rgba(255,255,255,0.92)', textShadow: '0 1px 4px rgba(0,0,0,0.25)' }}>{s.subtitle}</div>
)}
</div>
)}
<TiltImage src={s.src} alt={s.alt} enabled={index === idx && !!s.followMouseTilt} maxDeg={s.tiltMaxDeg ?? 6} />
</div>
))}
{/* Dot navigation */}
<div
style={{
position: 'absolute',
bottom: 16,
left: 0,
right: 0,
display: 'flex',
justifyContent: 'center',
gap: 10,
zIndex: 2,
}}
>
{Array.from({ length: totalSlides }).map((_, i) => (
<button
key={i}
aria-label={`Go to slide ${i + 1}`}
onClick={() => setIndex(i)}
style={{
width: '10px',
height: '12px',
borderRadius: '50%',
border: 'none',
cursor: 'pointer',
backgroundColor: i === index ? '#ffffff' : 'rgba(255,255,255,0.5)',
boxShadow: '0 2px 6px rgba(0,0,0,0.25)',
display: 'block',
flexShrink: 0,
}}
/>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,43 @@
import { BASE_PATH } from '../../constants/app';
export type LoginCarouselSlide = {
src: string
alt?: string
title?: string
subtitle?: string
cornerModelUrl?: string
followMouseTilt?: boolean
tiltMaxDeg?: number
}
export const loginSlides: LoginCarouselSlide[] = [
{
src: `${BASE_PATH}/Login/Firstpage.png`,
alt: 'Stirling PDF overview',
title: 'Your one-stop-shop for all your PDF needs.',
subtitle:
'A privacy-first cloud suite for PDFs that lets you convert, sign, redact, and manage documents, along with 50+ other powerful tools.',
followMouseTilt: true,
tiltMaxDeg: 5,
},
{
src: `${BASE_PATH}/Login/AddToPDF.png`,
alt: 'Edit PDFs',
title: 'Edit PDFs to display/secure the information you want',
subtitle:
'With over a dozen tools to help you redact, sign, read and manipulate PDFs, you will be sure to find what you are looking for.',
followMouseTilt: true,
tiltMaxDeg: 5,
},
{
src: `${BASE_PATH}/Login/SecurePDF.png`,
alt: 'Secure PDFs',
title: 'Protect sensitive information in your PDFs',
subtitle:
'Add passwords, redact content, and manage certificates with ease.',
followMouseTilt: true,
tiltMaxDeg: 5,
},
]
export default loginSlides

View File

@@ -102,8 +102,8 @@ export default function Login() {
setError(error.message)
} else if (user && session) {
console.log('[Login] Email sign in successful')
// Navigate to home page
navigate('/')
// Auth state will update automatically and Landing will redirect to home
// No need to navigate manually here
}
} catch (err) {
console.error('[Login] Unexpected error:', err)

View File

@@ -1,4 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'
import LoginRightCarousel from '../../components/shared/LoginRightCarousel'
import loginSlides from '../../components/shared/loginSlides'
import styles from './AuthLayout.module.css'
interface AuthLayoutProps {
@@ -58,11 +60,7 @@ export default function AuthLayout({ children }: AuthLayoutProps) {
</div>
</div>
{!hideRightPanel && (
<div style={{
backgroundImage: `url(${window.location.origin}/images/auth-background.jpg)`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}} />
<LoginRightCarousel imageSlides={loginSlides} initialSeconds={5} slideSeconds={8} />
)}
</div>
</div>

View File

@@ -29,8 +29,13 @@ export default function EmailPasswordForm({
}: EmailPasswordFormProps) {
const { t } = useTranslation()
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSubmit()
}
return (
<>
<form onSubmit={handleSubmit}>
<div className="auth-fields">
<div className="auth-field">
<label htmlFor="email" className="auth-label">{t('login.email')}</label>
@@ -70,12 +75,12 @@ export default function EmailPasswordForm({
</div>
<button
onClick={onSubmit}
type="submit"
disabled={isSubmitting || !email || (showPasswordField && !password)}
className="auth-button"
>
{submitButtonText}
</button>
</>
</form>
)
}

View File

@@ -262,6 +262,27 @@
--modal-content-bg: #ffffff;
--modal-header-border: rgba(0, 0, 0, 0.06);
/* Auth page colors (light mode only - auth pages force light mode) */
--auth-bg-color-light-only: #f3f4f6;
--auth-card-bg: #ffffff;
--auth-card-bg-light-only: #ffffff;
--auth-label-text-light-only: #374151;
--auth-input-border-light-only: #d1d5db;
--auth-input-bg-light-only: #ffffff;
--auth-input-text-light-only: #111827;
--auth-border-focus-light-only: #3b82f6;
--auth-focus-ring-light-only: rgba(59, 130, 246, 0.1);
--auth-button-bg-light-only: #AF3434;
--auth-button-text-light-only: #ffffff;
--auth-magic-button-bg-light-only: #e5e7eb;
--auth-magic-button-text-light-only: #374151;
--auth-text-primary-light-only: #111827;
--auth-text-secondary-light-only: #6b7280;
--text-divider-rule-rgb-light: 229, 231, 235;
--text-divider-label-rgb-light: 156, 163, 175;
--tool-subcategory-rule-color-light: #e5e7eb;
--tool-subcategory-text-color-light: #9ca3af;
/* PDF Report Colors (always light) */
--pdf-light-header-bg: 239 246 255;
--pdf-light-accent: 59 130 246;
@@ -480,7 +501,7 @@
/* Tool panel search bar background colors (dark mode) */
--tool-panel-search-bg: #1F2329;
--tool-panel-search-border-bottom: #4B525A;
--information-text-bg: #292e34;
--information-text-color: #ececec;