@@ -257,8 +257,8 @@ export default function TokenList() {
{/* Message Box for no results */}
{(!data?.pages?.length || !data?.pages[0].items.length) && !isFetching && (
-
-
No Token Sales
+
+
No Token Sales
No tokens found matching your criteria.
)}
@@ -286,9 +286,9 @@ export default function TokenList() {
ref={loadMoreBtn}
onClick={() => fetchNextPage()}
disabled={isFetching}
- className={`px-6 py-3 rounded-full border-none text-white cursor-pointer text-base font-semibold tracking-wide transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] ${isFetching
- ? 'bg-white/10 cursor-not-allowed opacity-60'
- : 'bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 border-0 shadow-lg hover:shadow-xl transition-all duration-300'
+ className={`px-6 py-3 rounded-full border-none cursor-pointer text-base font-semibold tracking-wide transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] ${isFetching
+ ? 'bg-gray-200 text-gray-500 cursor-not-allowed opacity-60'
+ : 'bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white border-0 shadow-lg hover:shadow-xl transition-all duration-300'
}`}
>
{isFetching ? (
diff --git a/src/main.tsx b/src/main.tsx
index 939f68744..2133ef1de 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -10,6 +10,8 @@ import ErrorBoundary from './components/ErrorBoundary';
import ToastProvider from './components/ToastProvider';
import { AeSdkProvider } from './context/AeSdkProvider';
import { AePricePollingProvider } from './context/AePricePollingProvider';
+import { ThemeProvider } from './contexts/ThemeContext';
+import { OnboardingProvider } from './contexts/OnboardingContext';
import './i18n';
import './styles/base.scss';
import './styles/tailwind.css';
@@ -44,9 +46,13 @@ const queryClient = new QueryClient({
-
-
-
+
+
+
+
+
+
+
diff --git a/src/routes.tsx b/src/routes.tsx
index 0841e0c8b..dcceab568 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -1,8 +1,15 @@
import React, { lazy } from "react";
import { RouteObject, Navigate, useParams } from "react-router-dom";
-import SocialLayout from "./components/layout/SocialLayout";
+import AppLayout from "./components/layout/AppLayout";
+// New Home page
+const Home = lazy(() => import("./views/Home"));
+
+// Social components
const FeedList = lazy(() => import("./features/social/views/FeedList"));
+const PostDetail = lazy(() => import("./features/social/views/PostDetail"));
+const UserProfile = lazy(() => import("./views/UserProfile"));
+
const NotFound = lazy(() => import("./views/NotFound"));
const TokenList = lazy(() => import("./features/trending/views/TokenList"));
const TrendCloudVisx = lazy(() => import("./views/Trendminer/TrendCloudVisx"));
@@ -26,8 +33,6 @@ const LeaderboardView = lazy(
const TokenSaleDetails = lazy(
() => import("./features/trending/views/TokenSaleDetails")
);
-const PostDetail = lazy(() => import("./features/social/views/PostDetail"));
-const UserProfile = lazy(() => import("./views/UserProfile"));
const Landing = lazy(() => import("./views/Landing"));
const Conference = lazy(() => import("./views/Conference"));
const Governance = lazy(() => import("./views/Governance"));
@@ -91,167 +96,162 @@ function NavigateUserProfile() {
}
export const routes: RouteObject[] = [
+ // All routes wrapped in AppLayout
{
path: "/",
- element:
,
+ element:
,
children: [
- { index: true, element:
},
- // Post routes - slug-based (also handles IDs, which will redirect in PostDetail)
- { path: "post/:slug", element:
},
+ // New landing page (Topics-focused)
+ { index: true, element:
},
+
+ // Social routes (moved from root)
+ { path: "social", element:
},
+ { path: "post/:slug", element:
},
+ { path: "post/:slug/comment/:id", element:
},
+ { path: "users/:address", element:
},
+
+ // Topics/Trends routes
+ { path: "trends/tokens", element:
},
+ { path: "trends", element:
},
+ { path: "trends/visx", element:
},
+ { path: "trends/tokens/:tokenName", element:
},
+ { path: "trends/leaderboard", element:
},
+ { path: "trends/invite", element:
},
+ { path: "trends/daos", element:
},
+ { path: "trends/dao/:saleAddress", element:
},
+ { path: "trends/dao/:saleAddress/vote/:voteId/:voteAddress", element:
},
+ { path: "trends/accounts", element:
},
+ { path: "trends/accounts/:address", element:
},
+ { path: "trends/create", element:
},
+
+ // DeFi routes
+ { path: "defi", element:
},
{
- path: "post/:slug/comment/:id",
- element:
,
+ path: "defi/swap",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/wrap",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/buy-ae-with-eth",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/bridge",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/pool",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/pool/add-tokens",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/explore/tokens",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/explore/tokens/:tokenAddress",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/explore/pools",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/explore/pools/:poolAddress",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "defi/explore/transactions",
+ element: (
+
+
+
+ ),
},
- { path: "users/:address", element:
},
- ],
- },
- // New trends routes
- { path: "/trends/tokens", element:
},
- { path: "/trends", element:
},
- { path: "/trends/visx", element:
},
- { path: "/trends/tokens/:tokenName", element:
},
- { path: "/trends/leaderboard", element:
},
- { path: "/tx-queue/:id", element:
},
- { path: "/trends/invite", element:
},
- { path: "/trends/daos", element:
},
- { path: "/trends/dao/:saleAddress", element:
},
- { path: "/trends/dao/:saleAddress/vote/:voteId/:voteAddress", element:
},
- { path: "/trends/accounts", element:
},
- { path: "/trends/accounts/:address", element:
},
- { path: "/trends/create", element:
},
- // Legacy redirects from /trending/* -> /trends/*
- { path: "/trending", element:
},
- { path: "/trending/tokens", element:
},
- { path: "/trending/visx", element:
},
- { path: "/trending/invite", element:
},
- { path: "/trending/daos", element:
},
- { path: "/trending/accounts", element:
},
- { path: "/trending/create", element:
},
- { path: "/trending/leaderboard", element:
},
- // Param redirects using small wrappers
- { path: "/trending/tokens/:tokenName", element:
},
- { path: "/trending/dao/:saleAddress", element:
},
- { path: "/trending/dao/:saleAddress/vote/:voteId/:voteAddress", element:
},
- { path: "/trending/accounts/:address", element:
},
- // Redirect /user/* to /users/* for consistency
- {
- path: "/user/:address",
- element:
,
- },
- { path: "/landing", element:
},
- { path: "/meet/:room?", element:
},
- { path: "/voting", element:
},
- { path: "/voting/p/:id", element:
},
- { path: "/voting/account", element:
},
- { path: "/voting/create", element:
},
- // New DEX Routes with Layout
- {
- path: "/defi",
- element:
,
- },
- {
- path: "/defi/swap",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/wrap",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/buy-ae-with-eth",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/bridge",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/pool",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/pool/add-tokens",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/explore/tokens",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/explore/tokens/:tokenAddress",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/explore/pools",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/explore/pools/:poolAddress",
- element: (
-
-
-
- ),
- },
- {
- path: "/defi/explore/transactions",
- element: (
-
-
-
- ),
- },
+ // Other routes
+ { path: "tx-queue/:id", element:
},
+ { path: "landing", element:
},
+ { path: "meet/:room?", element:
},
+ { path: "voting", element:
},
+ { path: "voting/p/:id", element:
},
+ { path: "voting/account", element:
},
+ { path: "voting/create", element:
},
+ { path: "terms", element:
},
+ { path: "privacy", element:
},
+ { path: "faq", element:
},
- // Legacy DEX Routes (for backward compatibility)
- { path: "/swap", element:
},
- { path: "/pool", element:
},
- { path: "/explore", element:
},
- { path: "/explore/tokens/:id", element:
},
- { path: "/explore/pools/:id", element:
},
- { path: "/pool/add-tokens", element:
},
+ // Legacy redirects
+ { path: "trending", element:
},
+ { path: "trending/tokens", element:
},
+ { path: "trending/visx", element:
},
+ { path: "trending/invite", element:
},
+ { path: "trending/daos", element:
},
+ { path: "trending/accounts", element:
},
+ { path: "trending/create", element:
},
+ { path: "trending/leaderboard", element:
},
+ { path: "trending/tokens/:tokenName", element:
},
+ { path: "trending/dao/:saleAddress", element:
},
+ { path: "trending/dao/:saleAddress/vote/:voteId/:voteAddress", element:
},
+ { path: "trending/accounts/:address", element:
},
+ { path: "user/:address", element:
},
- { path: "/terms", element:
},
- { path: "/privacy", element:
},
- { path: "/faq", element:
},
- {
- path: "*",
- element:
,
+ // Legacy DEX Routes
+ { path: "swap", element:
},
+ { path: "pool", element:
},
+ { path: "explore", element:
},
+ { path: "explore/tokens/:id", element:
},
+ { path: "explore/pools/:id", element:
},
+ { path: "pool/add-tokens", element:
},
+
+ // 404
+ { path: "*", element:
},
+ ],
},
];
diff --git a/src/styles/base.scss b/src/styles/base.scss
index be39ad1f8..0e88a3033 100644
--- a/src/styles/base.scss
+++ b/src/styles/base.scss
@@ -2,6 +2,16 @@
@use './mixins.scss' as *;
@use './mobile-optimizations.scss' as *;
@import './tailwind.css';
+@import './themes.scss';
+
+// Import Space Grotesk for distinctive headings
+@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap');
+
+// Typography - Use Space Grotesk for headings
+h1, h2, h3, .font-display {
+ font-family: 'Space Grotesk', Inter, -apple-system, BlinkMacSystemFont, sans-serif;
+ letter-spacing: -0.02em;
+}
:root {
color-scheme: dark;
diff --git a/src/styles/themes.scss b/src/styles/themes.scss
new file mode 100644
index 000000000..acfca2055
--- /dev/null
+++ b/src/styles/themes.scss
@@ -0,0 +1,95 @@
+// Section-specific color theming for Superhero
+// Each major section (Topics, Social, DeFi) has its own color palette
+
+:root {
+ // Topics (Trends) - Amber/Orange theme
+ --topics-primary: #F59E0B;
+ --topics-primary-light: #FCD34D;
+ --topics-primary-dark: #D97706;
+ --topics-gradient: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
+ --topics-bg-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.05) 100%);
+ --topics-border: rgba(245, 158, 11, 0.3);
+
+ // Social - Cyan/Teal theme
+ --social-primary: #06B6D4;
+ --social-primary-light: #67E8F9;
+ --social-primary-dark: #0891B2;
+ --social-gradient: linear-gradient(135deg, #06B6D4 0%, #0891B2 100%);
+ --social-bg-gradient: linear-gradient(135deg, rgba(6, 182, 212, 0.1) 0%, rgba(8, 145, 178, 0.05) 100%);
+ --social-border: rgba(6, 182, 212, 0.3);
+
+ // DeFi - Violet/Indigo theme
+ --defi-primary: #8B5CF6;
+ --defi-primary-light: #C4B5FD;
+ --defi-primary-dark: #6366F1;
+ --defi-gradient: linear-gradient(135deg, #8B5CF6 0%, #6366F1 100%);
+ --defi-bg-gradient: linear-gradient(135deg, rgba(139, 92, 246, 0.1) 0%, rgba(99, 102, 241, 0.05) 100%);
+ --defi-border: rgba(139, 92, 246, 0.3);
+
+ // Dynamic section variables (set via JS in AppLayout)
+ --section-primary: var(--topics-primary);
+ --section-primary-light: var(--topics-primary-light);
+ --section-gradient: var(--topics-gradient);
+ --section-border: var(--topics-border);
+}
+
+// Section-specific body backgrounds (optional enhancement)
+body[data-section="topics"] {
+ --section-primary: var(--topics-primary);
+ --section-primary-light: var(--topics-primary-light);
+ --section-gradient: var(--topics-gradient);
+ --section-border: var(--topics-border);
+}
+
+body[data-section="social"] {
+ --section-primary: var(--social-primary);
+ --section-primary-light: var(--social-primary-light);
+ --section-gradient: var(--social-gradient);
+ --section-border: var(--social-border);
+}
+
+body[data-section="defi"] {
+ --section-primary: var(--defi-primary);
+ --section-primary-light: var(--defi-primary-light);
+ --section-gradient: var(--defi-gradient);
+ --section-border: var(--defi-border);
+}
+
+// Utility classes for section theming
+.section-text {
+ color: var(--section-primary);
+}
+
+.section-bg {
+ background: var(--section-gradient);
+}
+
+.section-border {
+ border-color: var(--section-border);
+}
+
+.section-bg-subtle {
+ background: linear-gradient(135deg,
+ rgba(var(--section-primary), 0.1) 0%,
+ rgba(var(--section-primary), 0.02) 100%
+ );
+}
+
+// Topics-specific styles
+.topics-theme {
+ --local-primary: var(--topics-primary);
+ --local-gradient: var(--topics-gradient);
+}
+
+// Social-specific styles
+.social-theme {
+ --local-primary: var(--social-primary);
+ --local-gradient: var(--social-gradient);
+}
+
+// DeFi-specific styles
+.defi-theme {
+ --local-primary: var(--defi-primary);
+ --local-gradient: var(--defi-gradient);
+}
+
diff --git a/src/views/Home.tsx b/src/views/Home.tsx
new file mode 100644
index 000000000..2e95091e5
--- /dev/null
+++ b/src/views/Home.tsx
@@ -0,0 +1,178 @@
+import React, { useEffect, useRef, useState, useMemo } from "react";
+import { useInfiniteQuery } from "@tanstack/react-query";
+import HeroSection from "../components/landing/HeroSection";
+import FeaturedTopics from "../components/landing/FeaturedTopics";
+import LiveActivityTicker from "../components/landing/LiveActivityTicker";
+import EnhancedTokenList from "../components/landing/EnhancedTokenList";
+import Head from "../seo/Head";
+import { useSectionTheme } from "@/components/layout/AppLayout";
+import { useTheme } from "@/contexts/ThemeContext";
+import { TokensService } from "@/api/generated";
+import { useAccount } from "@/hooks";
+import Spinner from "@/components/Spinner";
+
+type SortOption = 'trending_score' | 'market_cap' | 'price' | 'newest' | 'holders_count';
+
+export default function Home() {
+ const { colors } = useSectionTheme();
+ const { isDark } = useTheme();
+ const { activeAccount } = useAccount();
+
+ // Scroll tracking for section transition
+ const [hasScrolledPastHero, setHasScrolledPastHero] = useState(false);
+ const sectionRef = useRef
(null);
+
+ // Token list state
+ const [orderBy, setOrderBy] = useState('trending_score');
+ const [search, setSearch] = useState("");
+ const [searchThrottled, setSearchThrottled] = useState("");
+ const loadMoreBtn = useRef(null);
+
+ // Track scroll position
+ useEffect(() => {
+ const handleScroll = () => {
+ if (sectionRef.current) {
+ const rect = sectionRef.current.getBoundingClientRect();
+ setHasScrolledPastHero(rect.top <= 100);
+ }
+ };
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ // Throttle search input
+ useEffect(() => {
+ const timeoutId = setTimeout(() => {
+ setSearchThrottled(search);
+ }, 500);
+ return () => clearTimeout(timeoutId);
+ }, [search]);
+
+ const orderByMapped = useMemo(() => {
+ if (orderBy === 'newest') {
+ return 'created_at';
+ }
+ return orderBy;
+ }, [orderBy]);
+
+ const finalOrderDirection = useMemo((): 'ASC' | 'DESC' => {
+ if (orderBy === 'newest') return 'DESC';
+ return 'DESC';
+ }, [orderBy]);
+
+ const { data, isFetching, fetchNextPage, hasNextPage } = useInfiniteQuery({
+ initialPageParam: 1,
+ queryFn: ({ pageParam = 1 }) =>
+ TokensService.listAll({
+ orderBy: orderByMapped as any,
+ orderDirection: finalOrderDirection,
+ search: searchThrottled || undefined,
+ limit: 20,
+ page: pageParam,
+ }),
+ getNextPageParam: (lastPage: any, allPages, lastPageParam) =>
+ lastPage?.meta?.currentPage === lastPage?.meta?.totalPages
+ ? undefined
+ : lastPageParam + 1,
+ queryKey: [
+ "Home.TokenList",
+ orderBy,
+ orderByMapped,
+ finalOrderDirection,
+ searchThrottled,
+ ],
+ staleTime: 1000 * 60,
+ });
+
+ // Flatten all token items from pages
+ const allTokens = useMemo(() =>
+ data?.pages?.flatMap(page => page.items) || [],
+ [data?.pages]
+ );
+
+ // Intersection observer for infinite loading
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.intersectionRatio === 1 && hasNextPage && !isFetching) {
+ fetchNextPage();
+ }
+ },
+ { threshold: 1 }
+ );
+ if (loadMoreBtn.current) {
+ observer.observe(loadMoreBtn.current);
+ }
+ return () => observer.disconnect();
+ }, [hasNextPage, isFetching, fetchNextPage]);
+
+ return (
+
+
+
+ {/* Hero Section */}
+
+
+ {/* Live Activity Ticker */}
+
+
+ {/* Featured Topics/Hashtags Grid */}
+
+
+ {/* Full Token List - Continuous Scroll */}
+
+ {/* Enhanced Token List */}
+
+
+ {/* Load More Button */}
+ {hasNextPage && (
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/views/TokenDetail.tsx b/src/views/TokenDetail.tsx
index 427571da3..aa4bb5ea9 100644
--- a/src/views/TokenDetail.tsx
+++ b/src/views/TokenDetail.tsx
@@ -10,6 +10,7 @@ import { useAeSdk } from "../hooks";
import { Decimal } from "../libs/decimal";
import Spinner from "../components/Spinner";
import { getPairsByTokenUsd, getTokenWithUsd } from "../libs/dexBackend";
+import { useSectionTheme } from "@/components/layout/AppLayout";
interface TokenData {
address: string;
@@ -41,6 +42,7 @@ export default function TokenDetail() {
const { activeNetwork } = useAeSdk();
const { tokenAddress } = useParams();
const navigate = useNavigate();
+ const { colors } = useSectionTheme();
const [token, setToken] = useState