From a938fb4ec5d04c8498663de5b8426e745d9ebdb6 Mon Sep 17 00:00:00 2001
From: freepilot-bot <215356755+freepilot-bot@users.noreply.github.com>
Date: Fri, 13 Jun 2025 12:46:43 +0000
Subject: [PATCH] feat: add intermediate loading page for app views
- Create new AppLoadingPage component with 5-second countdown
- Update routing to serve /apps/:id/view with intermediate page
- Replace all /api/apps/:id/view links with /apps/:id/view
- Add LNFly branding and AI disclaimer to loading page
- Include mobile-responsive design with progress bar
- Support preview key parameter forwarding
- Automatically redirect to actual app after countdown
- Add instant view button for immediate access
---
frontend/src/components/ExploreApps.tsx | 2 +-
frontend/src/main.tsx | 7 +-
frontend/src/pages/AppLoadingPage.tsx | 116 ++++++++++++++++++++++++
frontend/src/pages/AppStatusPage.tsx | 2 +-
4 files changed, 124 insertions(+), 3 deletions(-)
create mode 100644 frontend/src/pages/AppLoadingPage.tsx
diff --git a/frontend/src/components/ExploreApps.tsx b/frontend/src/components/ExploreApps.tsx
index 4f7aafa..b1d1471 100644
--- a/frontend/src/components/ExploreApps.tsx
+++ b/frontend/src/components/ExploreApps.tsx
@@ -270,7 +270,7 @@ function ExploreApps({ onFork }: ExploreAppsProps) {
)}
-
+
, // App status page
},
- // Remove the route for /apps/:id/view as it will be handled by the backend directly
+ {
+ path: "/apps/:id/view",
+ element: , // Intermediate loading page
+ },
+ // Backend will handle /api/apps/:id/view directly
]);
createRoot(document.getElementById("root")!).render(
diff --git a/frontend/src/pages/AppLoadingPage.tsx b/frontend/src/pages/AppLoadingPage.tsx
new file mode 100644
index 0000000..6c2f4c8
--- /dev/null
+++ b/frontend/src/pages/AppLoadingPage.tsx
@@ -0,0 +1,116 @@
+import { useEffect, useState } from "react";
+import { useNavigate, useParams, useSearchParams } from "react-router-dom";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent } from "@/components/ui/card";
+import Logo from "@/assets/logo.svg";
+
+interface AppLoadingPageProps {}
+
+function AppLoadingPage({}: AppLoadingPageProps) {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const [countdown, setCountdown] = useState(5);
+
+ // Get preview key from URL params
+ const previewKey = searchParams.get('previewKey');
+
+ // Build the actual app URL with preview key if needed
+ const appUrl = `/api/apps/${id}/view${previewKey ? `?previewKey=${previewKey}` : ''}`;
+
+ useEffect(() => {
+ // Update countdown every second
+ const interval = setInterval(() => {
+ setCountdown((prev) => {
+ if (prev <= 1) {
+ // Countdown finished, redirect to actual app
+ window.location.href = appUrl;
+ return 0;
+ }
+ return prev - 1;
+ });
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [appUrl]);
+
+ const handleViewNow = () => {
+ // Immediately redirect to the app
+ window.location.href = appUrl;
+ };
+
+ const handleGoHome = () => {
+ navigate("/");
+ };
+
+ return (
+
+ {/* Background logo */}
+

+
+
+
+
+
+ {/* Main heading */}
+
+ App created with{" "}
+
+
+
+ {/* Subtitle explaining what LNFly is */}
+
+ AI-powered app creation platform
+
+
+ {/* Warning message */}
+
+
⚠️ Important Notice
+
Apps on LNFly are vibe coded with AI. Use at your own risk.
+
+
+ {/* Countdown display */}
+
+
+ Automatically loading app in:
+
+
+ {countdown}
+
+
+
+ {countdown > 0 ? `${countdown} second${countdown !== 1 ? 's' : ''} remaining` : 'Redirecting...'}
+
+
+
+ {/* Action button */}
+
+
+
+
+
+
+ );
+}
+
+export default AppLoadingPage;
\ No newline at end of file
diff --git a/frontend/src/pages/AppStatusPage.tsx b/frontend/src/pages/AppStatusPage.tsx
index 07de8fa..81686e1 100644
--- a/frontend/src/pages/AppStatusPage.tsx
+++ b/frontend/src/pages/AppStatusPage.tsx
@@ -1102,7 +1102,7 @@ function AppStatusPage() {
href={
buttonDisabled
? "#"
- : `/api/apps/${id}/view${
+ : `/apps/${id}/view${
appData.published ? "" : `?previewKey=${previewKey}`
}`
}