234 lines
8.2 KiB
JavaScript
234 lines
8.2 KiB
JavaScript
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
|
import { motion } from "framer-motion";
|
|
import SEOHead from "./components/SEOHead";
|
|
import MobileOptimizedHeader from "./components/MobileOptimizedHeader";
|
|
import EnhancedTextInput from "./components/EnhancedTextInput";
|
|
import ImprovedCategoryFilter from "./components/ImprovedCategoryFilter";
|
|
import PerformanceOptimizedFontCard from "./components/PerformanceOptimizedFontCard";
|
|
import InfoSection from "./components/InfoSection";
|
|
import SocialButtons from "./components/SocialButtons";
|
|
import {
|
|
fontTransforms,
|
|
getFontsByCategory,
|
|
transformText,
|
|
getPopularFonts,
|
|
} from "./components/FontTransforms";
|
|
|
|
// kleine Helper
|
|
const sArr = (v) => (Array.isArray(v) ? v : []);
|
|
const sStr = (v) => (v ?? "").toString();
|
|
const sObj = (v) => (v ?? {});
|
|
|
|
export default function Home() {
|
|
const [inputText, setInputText] = useState("Hello Instagram!");
|
|
const [selectedCategory, setSelectedCategory] = useState("all");
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
const [animationsEnabled, setAnimationsEnabled] = useState(() => {
|
|
if (typeof window !== "undefined") {
|
|
const hasReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
const cores = typeof navigator !== "undefined" ? navigator.hardwareConcurrency : 8;
|
|
const isLowEndDevice = (cores ?? 8) < 4;
|
|
return !hasReducedMotion && !isLowEndDevice;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Mobile Detection
|
|
useEffect(() => {
|
|
const checkMobile = () => {
|
|
if (typeof window !== "undefined") {
|
|
setIsMobile(
|
|
window.innerWidth < 768 ||
|
|
/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
|
|
);
|
|
}
|
|
};
|
|
checkMobile();
|
|
if (typeof window !== "undefined") {
|
|
window.addEventListener("resize", checkMobile);
|
|
return () => window.removeEventListener("resize", checkMobile);
|
|
}
|
|
}, []);
|
|
|
|
// Debounce
|
|
const [debouncedText, setDebouncedText] = useState(inputText);
|
|
useEffect(() => {
|
|
const delay = isMobile ? 200 : 100;
|
|
const t = setTimeout(() => setDebouncedText(inputText), delay);
|
|
return () => clearTimeout(t);
|
|
}, [inputText, isMobile]);
|
|
|
|
// Fonts
|
|
const availableFonts = useMemo(
|
|
() => sArr(getFontsByCategory?.(selectedCategory)),
|
|
[selectedCategory]
|
|
);
|
|
const popularFonts = useMemo(() => sArr(getPopularFonts?.()), []);
|
|
|
|
const fontCounts = useMemo(() => {
|
|
const totalFonts = Object.keys(sObj(fontTransforms)).length;
|
|
const counts = { all: totalFonts };
|
|
Object.values(sObj(fontTransforms)).forEach((font) => {
|
|
const cat = font?.category ?? "other";
|
|
counts[cat] = (counts[cat] || 0) + 1;
|
|
});
|
|
return counts;
|
|
}, []);
|
|
|
|
// Analytics
|
|
const handleFontCopy = useCallback((fontName, text) => {
|
|
if (typeof window !== "undefined" && window.gtag) {
|
|
window.gtag("event", "font_copied", {
|
|
font_name: fontName,
|
|
text_length: sStr(text).length,
|
|
category: fontTransforms?.[fontName]?.category,
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
const handleQuickShare = useCallback(async () => {
|
|
if (typeof window === "undefined") return;
|
|
|
|
const shareData = {
|
|
title: "FancyText - Cool Fonts! 🔥",
|
|
text: "Check out this app for cool Instagram & TikTok fonts! 30+ fonts for free ✨",
|
|
url: window.location.href,
|
|
};
|
|
|
|
if (navigator.share) {
|
|
try {
|
|
await navigator.share(shareData);
|
|
} catch (err) {
|
|
if (err.name !== "AbortError") console.error("Share failed:", err);
|
|
}
|
|
} else {
|
|
try {
|
|
await navigator.clipboard.writeText(`${shareData.text}\n${shareData.url}`);
|
|
alert("Link copied! 📋");
|
|
} catch (err) {
|
|
console.error("Copy failed:", err);
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-pink-500 via-purple-600 to-blue-600 relative overflow-hidden">
|
|
<SEOHead currentText={inputText} />
|
|
|
|
{/* Google Fonts */}
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500&family=Bebas+Neue&family=Poppins:wght@500&family=Pacifico&family=Dancing+Script&family=Anton&family=Orbitron&family=Playfair+Display&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
|
|
{/* Background blobs */}
|
|
{animationsEnabled && !isMobile && (
|
|
<>
|
|
<div className="absolute top-20 left-10 w-72 h-72 bg-pink-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob"></div>
|
|
<div className="absolute top-40 right-10 w-72 h-72 bg-purple-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob animation-delay-2000"></div>
|
|
<div className="absolute -bottom-8 left-20 w-72 h-72 bg-blue-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob animation-delay-4000"></div>
|
|
</>
|
|
)}
|
|
|
|
<div className="relative z-10 container mx-auto px-2 sm:px-4 py-4 sm:py-8 max-w-6xl">
|
|
<MobileOptimizedHeader
|
|
animationsEnabled={animationsEnabled}
|
|
onToggleAnimations={setAnimationsEnabled}
|
|
totalFonts={Object.keys(sObj(fontTransforms)).length}
|
|
onQuickShare={handleQuickShare}
|
|
/>
|
|
|
|
<EnhancedTextInput
|
|
text={inputText}
|
|
onChange={setInputText}
|
|
placeholder="✍️ Enter your text:"
|
|
/>
|
|
|
|
<ImprovedCategoryFilter
|
|
selectedCategory={selectedCategory}
|
|
onCategoryChange={setSelectedCategory}
|
|
fontCounts={fontCounts}
|
|
isMobile={isMobile}
|
|
/>
|
|
|
|
{/* Font Grid */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 mb-8 sm:mb-12">
|
|
{availableFonts.map((fontName, index) => (
|
|
<PerformanceOptimizedFontCard
|
|
key={fontName}
|
|
fontName={fontName}
|
|
transformedText={transformText(sStr(debouncedText), fontName) ?? ""}
|
|
category={fontTransforms?.[fontName]?.category ?? "other"}
|
|
isPopular={popularFonts.includes(fontName)}
|
|
animationsEnabled={animationsEnabled}
|
|
index={index}
|
|
onCopy={handleFontCopy}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<SocialButtons onShare={handleQuickShare} />
|
|
<InfoSection />
|
|
|
|
<footer className="text-center text-white/60 text-xs sm:text-sm mt-8 sm:mt-12 space-y-2 px-4">
|
|
<p>
|
|
© 2024 FancyText - The best <strong>font generator for Instagram</strong> and{" "}
|
|
<strong>TikTok</strong>
|
|
</p>
|
|
<p className="text-xs">
|
|
Create cool fonts for social media posts •{" "}
|
|
{Object.keys(sObj(fontTransforms)).length}+ unique fonts
|
|
</p>
|
|
<div className="flex flex-wrap justify-center gap-2 sm:gap-4 mt-4 text-xs text-white/40">
|
|
<span>SEO-optimized</span>
|
|
<span>•</span>
|
|
<span>Mobile-first</span>
|
|
<span>•</span>
|
|
<span>Performance-optimized</span>
|
|
<span>•</span>
|
|
<span>PWA-ready</span>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<style jsx>{`
|
|
.scrollbar-hide {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
.scrollbar-hide::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
.touch-manipulation {
|
|
touch-action: manipulation;
|
|
}
|
|
@keyframes blob {
|
|
0% { transform: translate(0px, 0px) scale(1); }
|
|
33% { transform: translate(30px, -50px) scale(1.1); }
|
|
66% { transform: translate(-20px, 20px) scale(0.9); }
|
|
100% { transform: translate(0px, 0px) scale(1); }
|
|
}
|
|
.animate-blob { animation: blob 7s infinite; }
|
|
.animation-delay-2000 { animation-delay: 2s; }
|
|
.animation-delay-4000 { animation-delay: 4s; }
|
|
@media (hover: hover) {
|
|
.hover-lift:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
|
}
|
|
}
|
|
@media (max-width: 640px) {
|
|
.text-responsive {
|
|
font-size: 1.25rem;
|
|
line-height: 1.4;
|
|
}
|
|
}
|
|
@media (display-mode: standalone) {
|
|
body { padding-top: env(safe-area-inset-top); }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|