Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://superhero.com/</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://superhero.com/trends/tokens</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://superhero.com/defi/swap</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://superhero.com/terms</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://superhero.com/privacy</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://superhero.com/faq</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
</urlset>
17 changes: 6 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { useAeSdk, useAccount, useWalletConnect } from "./hooks";
import { routes } from "./routes";
import "./styles/genz-components.scss";
import "./styles/mobile-optimizations.scss";
import AppHeader from "./components/layout/app-header";
import { useSuperheroChainNames } from "./hooks/useChainName";
import FeedbackButton from "./components/FeedbackButton";
import CreateHashtagFab from "./components/CreateHashtagFab";

const CookiesDialog = React.lazy(
() => import("./components/modals/CookiesDialog")
Expand Down Expand Up @@ -66,13 +65,9 @@ export default function App() {
}, [activeAccount]);

return (
<div className="app-container">

<>
<GlobalNewAccountEducation />
<AppHeader />
<div className="app-content">
<CollectInvitationLinkCard />
</div>
<CollectInvitationLinkCard />
<Suspense fallback={<div className="loading-fallback" />}>
<ModalProvider
registry={{
Expand All @@ -87,9 +82,9 @@ export default function App() {
/>
</Suspense>
<Suspense fallback={<div className="loading-fallback" />}>
<div className="app-routes-container">{useRoutes(routes as any)}</div>
{useRoutes(routes as any)}
</Suspense>
<FeedbackButton />
</div>
<CreateHashtagFab />
</>
);
}
37 changes: 28 additions & 9 deletions src/components/ConnectWalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useAeSdk, useWalletConnect, useModal } from '../hooks';
import Favicon from '../svg/favicon.svg?react';
import { AeButton } from './ui/ae-button';
import { cn } from '@/lib/utils';
import { useSectionTheme } from '@/components/layout/AppLayout';
import { useTheme } from '@/contexts/ThemeContext';

type Props = {
label?: string;
Expand All @@ -12,39 +14,56 @@ type Props = {
className?: string;
variant?: 'default' | 'dex';
muted?: boolean; // greyed-out appearance while still clickable
useThemeColors?: boolean; // Whether to use section theme colors
};

export function ConnectWalletButton({ label, block, style, className, variant = 'default', muted = false }: Props) {
export function ConnectWalletButton({ label, block, style, className, variant = 'default', muted = false, useThemeColors = true }: Props) {
const { t } = useTranslation('common');
const { activeAccount } = useAeSdk()
const { connectWallet, connectingWallet } = useWalletConnect()
const { openModal } = useModal();
const { colors } = useSectionTheme();
const { isDark } = useTheme();

const displayLabel = label || t('buttons.connectWallet');
const connectingText = t('buttons.connecting');

if (activeAccount) return null;

const dexClasses = cn(
// Mobile (default): superhero blue with card-like radius
'bg-[#1161FE] text-white border-none rounded-xl text-sm',
// Mobile (default): section gradient with card-like radius
'text-white border-none rounded-xl text-sm',
// Desktop+: elegant dark/glass pill with icon
'sm:bg-black/80 sm:text-white sm:border sm:border-white/10 sm:backdrop-blur-[10px] sm:hover:bg-black/70 sm:!rounded-full sm:text-sm',
isDark
? 'sm:bg-slate-800/80 sm:text-white sm:border sm:border-slate-600 sm:backdrop-blur-[10px] sm:hover:bg-slate-700/80 sm:!rounded-full sm:text-sm'
: 'sm:bg-black/80 sm:text-white sm:border sm:border-white/10 sm:backdrop-blur-[10px] sm:hover:bg-black/70 sm:!rounded-full sm:text-sm',
'sm:shadow-[0_8px_24px_rgba(0,0,0,0.35)] hover:sm:shadow-[0_12px_32px_rgba(0,0,0,0.45)]'
);

const baseClasses = cn(
'rounded-xl sm:rounded-full border-border bg-card backdrop-blur-sm backdrop-saturate-120 hover:bg-card/80 hover:shadow-md text-sm',
'sm:bg-card sm:hover:bg-card/80 sm:text-sm',
'bg-[#1161FE] text-white border-none rounded-xl sm:rounded-full'
'rounded-xl sm:rounded-full border-border backdrop-blur-sm backdrop-saturate-120 hover:shadow-md text-sm text-white border-none',
isDark
? 'sm:bg-slate-800/80 sm:hover:bg-slate-700/80 sm:text-sm'
: 'sm:bg-card sm:hover:bg-card/80 sm:text-sm'
);

const mutedClasses = cn(
'rounded-xl sm:rounded-full text-sm',
'bg-white/10 text-white/70 border border-white/10 hover:bg-white/10 hover:text-white/80',
isDark
? 'bg-slate-700/50 text-slate-400 border border-slate-600 hover:bg-slate-700/70 hover:text-slate-300'
: 'bg-white/10 text-white/70 border border-white/10 hover:bg-white/10 hover:text-white/80',
'shadow-none'
);

// Compute the final style with section theme colors
const computedStyle: React.CSSProperties = {
...style,
...(useThemeColors && !muted ? {
background: colors.gradient,
boxShadow: `0 4px 16px ${colors.primary}40`,
} : {})
};

return (
<AeButton
onClick={() => openModal({ name: 'connect-wallet' })}
Expand All @@ -54,7 +73,7 @@ export function ConnectWalletButton({ label, block, style, className, variant =
size={variant === 'dex' ? 'default' : 'default'}
fullWidth={block}
className={cn(muted ? mutedClasses : (variant === 'dex' ? dexClasses : baseClasses), className)}
style={style}
style={computedStyle}
>
<span className="hidden sm:inline-flex items-center gap-2">
<Favicon className="w-4 h-4" />
Expand Down
130 changes: 130 additions & 0 deletions src/components/CreateHashtagFab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useState, useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useSectionTheme } from './layout/AppLayout';
import { useTheme } from '@/contexts/ThemeContext';

export default function CreateHashtagFab() {
const [isVisible, setIsVisible] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const location = useLocation();
const navigate = useNavigate();
const { colors } = useSectionTheme();
const { isDark } = useTheme();

// Only show on home and trends pages
const shouldShowOnPage = location.pathname === '/' || location.pathname.startsWith('/trends');

useEffect(() => {
if (!shouldShowOnPage) {
setIsVisible(false);
return;
}

const handleScroll = () => {
// Show FAB after scrolling 300px (when hero button is likely out of view)
const scrollThreshold = 300;
setIsVisible(window.scrollY > scrollThreshold);
};

window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll(); // Check initial position

return () => window.removeEventListener('scroll', handleScroll);
}, [shouldShowOnPage]);

const handleClick = () => {
navigate('/trends/create');
};

if (!shouldShowOnPage) return null;

return (
<button
onClick={handleClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`
fixed bottom-6 right-6 z-50
flex items-center gap-2.5
rounded-2xl
shadow-2xl
transition-all duration-500 ease-out
hover:scale-105 active:scale-95
${isVisible
? 'translate-y-0 opacity-100'
: 'translate-y-20 opacity-0 pointer-events-none'
}
`}
style={{
background: colors.gradient,
padding: isHovered ? '18px 28px' : '18px 22px',
boxShadow: `0 10px 40px ${colors.primary}50, 0 0 0 ${isHovered ? '4px' : '0px'} ${colors.primaryLight}40`,
}}
aria-label="Create a Hashtag"
title="Create a Hashtag"
>
{/* Animated glow ring */}
<div
className="absolute inset-0 rounded-2xl animate-pulse opacity-50"
style={{
background: colors.gradient,
filter: 'blur(8px)',
zIndex: -1,
}}
/>

{/* Hashtag Icon with animation */}
<div className={`relative transition-transform duration-300 ${isHovered ? 'rotate-12 scale-110' : ''}`}>
<svg
className="w-7 h-7"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
style={{ color: isDark ? '#0f172a' : '#ffffff' }}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2.5}
d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"
/>
</svg>
{/* Plus indicator */}
<div
className={`
absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full
flex items-center justify-center
transition-all duration-300
${isHovered ? 'scale-110' : 'scale-100'}
`}
style={{
background: isDark ? '#0f172a' : '#ffffff',
boxShadow: '0 2px 8px rgba(0,0,0,0.2)'
}}
>
<svg
className="w-2.5 h-2.5"
fill="none"
stroke={colors.primary}
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M12 4v16m8-8H4" />
</svg>
</div>
</div>

{/* Text - expands on hover */}
<span
className={`
font-bold text-base whitespace-nowrap overflow-hidden
transition-all duration-300 ease-out
${isHovered ? 'max-w-[150px] opacity-100' : 'max-w-0 opacity-0'}
`}
style={{ color: isDark ? '#0f172a' : '#ffffff' }}
>
Create Hashtag
</span>
</button>
);
}

Loading