feat: Show loading spinner on landing page when in PWA mode (#96)

## Summary
- Prevents the landing page from briefly flashing before redirecting to
`/dashboard` when accessing from an installed PWA
- Detects standalone mode synchronously via a `useState` initializer so
the spinner renders on the very first paint
- Shows a centered loading spinner instead of the full landing page
while the redirect happens

## Test plan
- [x] Open the app as an installed PWA and verify a spinner shows
instead of the landing page before redirecting to dashboard
- [x] Open the landing page in a regular browser and verify the full
landing page renders normally with no spinner
This commit is contained in:
Víctor Falcón 2026-02-02 08:28:52 +01:00 committed by GitHub
parent 28f9432af4
commit 21d36bb53b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 25 additions and 9 deletions

View File

@ -1,6 +1,7 @@
import EncryptionVideoPlayer from '@/components/landing/encryption-video-player';
import Header from '@/components/partials/header';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';
import {
Tooltip,
TooltipContent,
@ -28,7 +29,7 @@ import {
TrendingUpIcon,
ZapIcon,
} from 'lucide-react';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
const LANDING_IMAGES = [
{
@ -160,13 +161,20 @@ export default function Welcome({
const planEntries = Object.entries(pricing.plans);
const visitTrackedRef = useRef(false);
useEffect(() => {
const isStandalone =
const [isPwa] = useState(() => {
if (typeof window === 'undefined') {
return false;
}
return (
window.matchMedia('(display-mode: standalone)').matches ||
('standalone' in navigator &&
(navigator as Navigator & { standalone: boolean }).standalone);
(navigator as Navigator & { standalone: boolean }).standalone)
);
});
if (isStandalone) {
useEffect(() => {
if (isPwa) {
router.visit('/dashboard');
return;
}
@ -178,7 +186,15 @@ export default function Welcome({
trackEvent(LEAD_FUNNEL_EVENT_UUID, {
step: 'Visit',
});
}, []);
}, [isPwa]);
if (isPwa) {
return (
<div className="flex h-screen items-center justify-center">
<Spinner className="size-8" />
</div>
);
}
return (
<>

View File

@ -29,7 +29,7 @@ abstract class TestCase extends BaseTestCase
*/
protected function setupEncryptionKey($page, ?string $key = null, bool $reload = true): void
{
$key = $key ?? $this->generateTestEncryptionKey();
$key ??= $this->generateTestEncryptionKey();
try {
$page->script("localStorage.setItem('encryption_key', ".json_encode($key).')');
} catch (\Exception) {
@ -39,7 +39,7 @@ abstract class TestCase extends BaseTestCase
}
if ($reload) {
$currentUrl = $page->url();
$page->navigate($currentUrl)->wait(1);
$page->navigate($currentUrl)->wait(1.5);
}
}
@ -48,7 +48,7 @@ abstract class TestCase extends BaseTestCase
*/
protected function visitWithEncryptionKey(string $url, ?string $key = null)
{
$key = $key ?? $this->generateTestEncryptionKey();
$key ??= $this->generateTestEncryptionKey();
$page = visit($url);
$page->script("localStorage.setItem('encryption_key', ".json_encode($key).')');
$page->navigate($url)->wait(1);