import { useState, useEffect } from "react"; import { useLocation } from "wouter"; import { Store, Search, Download, Star, ArrowLeft, Play, Check, Loader2, Sparkles, TrendingUp, Clock, Zap, } from "lucide-react"; import { haptics } from "@/lib/haptics"; import { PullToRefresh } from "@/components/mobile/PullToRefresh"; interface AethexApp { id: string; name: string; description: string; category: string; icon_url?: string; install_count: number; rating: number; rating_count: number; is_featured: boolean; tags: string[]; owner_id: string; source_code: string; compiled_js: string; } interface Installation { id: string; app_id: string; installed_at: string; last_used_at?: string; app: AethexApp; } export default function MobileAethexAppStore() { const [, navigate] = useLocation(); const [apps, setApps] = useState([]); const [installedApps, setInstalledApps] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [loading, setLoading] = useState(true); const [installing, setInstalling] = useState(null); const [running, setRunning] = useState(null); const [activeTab, setActiveTab] = useState<"browse" | "installed">("browse"); useEffect(() => { fetchApps(); fetchInstalledApps(); }, []); const fetchApps = async () => { try { const response = await fetch("/api/aethex/apps", { credentials: "include", }); const data = await response.json(); setApps(data.apps || []); } catch (error) { console.error("Failed to fetch apps:", error); } finally { setLoading(false); } }; const fetchInstalledApps = async () => { try { const response = await fetch("/api/aethex/apps/installed/my", { credentials: "include", }); const data = await response.json(); setInstalledApps(data.installations || []); } catch (error) { console.error("Failed to fetch installed apps:", error); } }; const handleRefresh = async () => { haptics.light(); await Promise.all([fetchApps(), fetchInstalledApps()]); haptics.success(); }; const handleInstall = async (appId: string, appName: string) => { haptics.medium(); setInstalling(appId); try { const response = await fetch(`/api/aethex/apps/${appId}/install`, { method: "POST", credentials: "include", }); const data = await response.json(); if (data.success) { haptics.success(); alert(`${appName} installed!`); fetchInstalledApps(); } else { haptics.error(); alert(data.error || "Failed to install"); } } catch (error) { console.error("Installation error:", error); haptics.error(); alert("Failed to install"); } finally { setInstalling(null); } }; const handleRun = async (appId: string, appName: string) => { haptics.medium(); setRunning(appId); try { const response = await fetch(`/api/aethex/apps/${appId}/run`, { method: "POST", credentials: "include", }); const data = await response.json(); if (data.success && data.compiled_code) { try { const sandbox = { console: { log: (...args: any[]) => { const output = args.map((a) => String(a)).join(" "); alert(`${appName}: ${output}`); }, }, alert: (msg: string) => alert(`${appName}: ${msg}`), }; new Function("console", "alert", data.compiled_code)(sandbox.console, sandbox.alert); haptics.success(); } catch (execError) { console.error("App execution error:", execError); alert(`Runtime error: ${execError}`); haptics.error(); } } else { alert(data.error || "Failed to run app"); haptics.error(); } } catch (error) { console.error("Run error:", error); alert("Failed to run app"); haptics.error(); } finally { setRunning(null); } }; const isInstalled = (appId: string) => { return installedApps.some((i) => i.app_id === appId); }; const filteredApps = apps.filter( (app) => app.name.toLowerCase().includes(searchQuery.toLowerCase()) || app.description?.toLowerCase().includes(searchQuery.toLowerCase()) ); const featuredApps = filteredApps.filter((app) => app.is_featured); const regularApps = filteredApps.filter((app) => !app.is_featured); return (
{/* Animated background */}
{/* Header */}

App Store

{apps.length} apps available

{/* Search Bar */}
setSearchQuery(e.target.value)} placeholder="Search apps..." className="w-full bg-cyan-500/10 border border-cyan-500/30 rounded-lg pl-10 pr-4 py-3 text-white placeholder-cyan-300/50 focus:outline-none focus:border-cyan-400" />
{/* Tab Navigation */}
{/* Content */}
{activeTab === "browse" && ( <> {loading ? (
) : ( <> {/* Featured Apps */} {featuredApps.length > 0 && (

Featured

{featuredApps.map((app) => ( ))}
)} {/* All Apps */} {regularApps.length > 0 && (

All Apps

{regularApps.map((app) => ( ))}
)} {filteredApps.length === 0 && (

No apps found

)} )} )} {activeTab === "installed" && ( <> {installedApps.length === 0 ? (

No installed apps

Browse apps to get started

) : (
{installedApps.map((installation) => ( ))}
)} )}
); } interface AppCardProps { app: AethexApp; isInstalled: boolean; installing: boolean; running: boolean; onInstall: (appId: string, appName: string) => void; onRun: (appId: string, appName: string) => void; featured?: boolean; showLastUsed?: boolean; lastUsedAt?: string; } function AppCard({ app, isInstalled, installing, running, onInstall, onRun, featured, showLastUsed, lastUsedAt, }: AppCardProps) { const getCategoryColor = (category: string) => { const colors: Record = { game: "from-pink-500/20 to-purple-500/20 border-pink-500/30", utility: "from-blue-500/20 to-cyan-500/20 border-blue-500/30", social: "from-green-500/20 to-emerald-500/20 border-green-500/30", education: "from-yellow-500/20 to-orange-500/20 border-yellow-500/30", }; return colors[category] || "from-gray-500/20 to-slate-500/20 border-gray-500/30"; }; return (

{app.name}

{featured && }

{app.description}

{/* Stats */}
{app.rating.toFixed(1)}
{app.install_count}
{app.category}
{/* Tags */} {app.tags && app.tags.length > 0 && (
{app.tags.slice(0, 3).map((tag) => ( {tag} ))}
)} {/* Last Used */} {showLastUsed && lastUsedAt && (
Last used: {new Date(lastUsedAt).toLocaleDateString()}
)} {/* Actions */}
{isInstalled ? ( <>
) : ( )}
); }