diff --git a/COPYRIGHT b/COPYRIGHT index ed0a785..c012935 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,8 +1,8 @@ Copyright: - © 2017-2025 Conceal Community - © 2018-2025 Conceal.Network, Conceal Team & Conceal Developers - © 2020-2025 Conceal DAO, Conceal Community & Conceal.Network + © 2017-2026 Conceal Community + © 2018-2026 Conceal.Network, Conceal Team & Conceal Developers + © 2020-2026 Conceal DAO, Conceal Community & Conceal.Network License: diff --git a/src/components/sections/RoadmapSection.tsx b/src/components/sections/RoadmapSection.tsx index 5cca3fd..054b48b 100644 --- a/src/components/sections/RoadmapSection.tsx +++ b/src/components/sections/RoadmapSection.tsx @@ -1,3 +1,4 @@ +import { useEffect, useRef, useState } from 'react'; import { AnimatedElement } from '../ui/AnimatedElement'; interface TimelineItem { @@ -5,6 +6,7 @@ interface TimelineItem { title: string; description: string; status: 'completed' | 'inprog' | 'activ' | 'done'; + url?: string; } const timelineItems: TimelineItem[] = [ @@ -136,6 +138,7 @@ const timelineItems: TimelineItem[] = [ title: 'Conceal Bridge', description: 'Swap your CCX to wCCX and back the other way with our Bridge tool.', status: 'completed', + url: 'https://bridge.conceal.network', }, { date: 'Q2 2021', @@ -162,6 +165,7 @@ const timelineItems: TimelineItem[] = [ title: 'Conceal Web Wallet', description: 'Release of our 100% Client-Side Web Wallet for Conceal.', status: 'completed', + url: 'https://wallet.conceal.network', }, { date: 'Q3 2023', @@ -169,6 +173,7 @@ const timelineItems: TimelineItem[] = [ description: 'Improving speed and making optimizations. Now send encrypted Messages from your smartphone', status: 'completed', + url: 'https://wallet.conceal.network', }, { date: 'Q3 2024', @@ -176,6 +181,7 @@ const timelineItems: TimelineItem[] = [ description: 'Improving anonymity by randomly picking nodes from a bigger list, now accessing SSL SmartNodes.', status: 'completed', + url: 'https://wallet.conceal.network', }, { date: 'Q3 2024', @@ -183,6 +189,7 @@ const timelineItems: TimelineItem[] = [ description: 'A great place to interact with other Concealers, Buy, Sell in a peer 2 peer way trading with your CCX!', status: 'completed', + url: 'https://conceal.network/marketplace', }, { date: 'Q4 2024', @@ -190,12 +197,28 @@ const timelineItems: TimelineItem[] = [ description: 'Now available in 14 languages. Access Deposits (view-only). Use qr code scanning feature to send messages. Get notified of new messages.', status: 'completed', + url: 'https://wallet.conceal.network', }, { - date: '', - title: 'Q2 2025, Web wallet deposits', + date: 'Q2 2025', + title: 'Web wallet deposits', description: 'Bringing deposits to client side web wallet', status: 'completed', + url: 'https://wallet.conceal.network', + }, + { + date: 'Q4 2025', + title: 'Conceal Labs', + description: 'Conceal Authenticator app is launched, your 2FA keys are now stored on the blockchain', + status: 'completed', + url: 'https://f-droid.org/en/packages/com.acktarius.concealauthenticator/', + }, + { + date: 'Q1 2026', + title: 'Conceal Labs', + description: 'Conceal-Faucet-API is launched, "one stop shop" for developpers to create faucet or game rewards', + status: 'completed', + url: 'https://github.com/ConcealNetwork/conceal-faucet-api', }, { date: '', @@ -230,8 +253,65 @@ const timelineItems: TimelineItem[] = [ ]; export function RoadmapSection() { + const sectionRef = useRef(null); + const itemRefs = useRef>(new Map()); + const [itemScales, setItemScales] = useState>(new Map()); + + useEffect(() => { + const handleScroll = () => { + if (!sectionRef.current) return; + + const sectionRect = sectionRef.current.getBoundingClientRect(); + + // Only apply effect if roadmap section is in viewport + const isSectionVisible = sectionRect.bottom > 0 && sectionRect.top < window.innerHeight; + + if (!isSectionVisible) { + // Reset all scales if section is not visible + const resetScales = new Map(); + itemRefs.current.forEach((_, index) => { + resetScales.set(index, 1.0); + }); + setItemScales(resetScales); + return; + } + + const newScales = new Map(); + const centerY = window.innerHeight / 2; + const maxDistance = 400; // Maximum distance for scaling effect + + itemRefs.current.forEach((element, index) => { + if (!element) return; + + const rect = element.getBoundingClientRect(); + const itemY = rect.top + rect.height / 2; + const distanceFromCenter = Math.abs(centerY - itemY); + + // Map distance to scale: 1.22 at center, 1.0 at maxDistance + const normalizedDistance = Math.min(1, distanceFromCenter / maxDistance); + const scale = 1.0 + (0.22 * (1 - normalizedDistance)); // 1.6 at center, 1.0 at maxDistance + + newScales.set(index, scale); + }); + + setItemScales(newScales); + }; + + // Initial check + handleScroll(); + + // Listen to scroll events + window.addEventListener('scroll', handleScroll, { passive: true }); + window.addEventListener('resize', handleScroll, { passive: true }); + + return () => { + window.removeEventListener('scroll', handleScroll); + window.removeEventListener('resize', handleScroll); + }; + }, []); + return ( -
+
{/* Background image */}
{ + if (el) { + itemRefs.current.set(index, el); + } else { + itemRefs.current.delete(index); + } + }} + className={`single-timeline flex items-center mb-[22px] transition-transform duration-300 ease-out ${isEven ? 'flex-row-reverse' : ''}`} + style={{ + transform: `scale(${scale})`, + transformOrigin: 'center center', + }} >
)} - {item.description && — {item.description}} + {item.description && — {item.url ? {item.description} : item.description}}