Skip to content
Merged
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
6 changes: 3 additions & 3 deletions COPYRIGHT
Original file line number Diff line number Diff line change
@@ -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:

Expand Down
103 changes: 98 additions & 5 deletions src/components/sections/RoadmapSection.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useEffect, useRef, useState } from 'react';
import { AnimatedElement } from '../ui/AnimatedElement';

interface TimelineItem {
date: string;
title: string;
description: string;
status: 'completed' | 'inprog' | 'activ' | 'done';
url?: string;
}

const timelineItems: TimelineItem[] = [
Expand Down Expand Up @@ -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',
Expand All @@ -162,40 +165,60 @@ 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',
title: 'Web wallet improvements',
description:
'Improving speed and making optimizations. Now send encrypted Messages from your smartphone',
status: 'completed',
url: 'https://wallet.conceal.network',
},
{
date: 'Q3 2024',
title: 'Web wallet improvements',
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',
title: 'Conceal Marketplace',
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',
title: 'Web wallet improvements',
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: '',
Expand Down Expand Up @@ -230,8 +253,65 @@ const timelineItems: TimelineItem[] = [
];

export function RoadmapSection() {
const sectionRef = useRef<HTMLElement>(null);
const itemRefs = useRef<Map<number, HTMLDivElement>>(new Map());
const [itemScales, setItemScales] = useState<Map<number, number>>(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<number, number>();
itemRefs.current.forEach((_, index) => {
resetScales.set(index, 1.0);
});
setItemScales(resetScales);
return;
}

const newScales = new Map<number, number>();
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 (
<section id="roadmap" className="py-16 px-4 border-b border-[rgba(255,255,255,0.2)] relative">
<section ref={sectionRef} id="roadmap" className="py-16 px-4 border-b border-[rgba(255,255,255,0.2)] relative">
{/* Background image */}
<div
id="herobg"
Expand Down Expand Up @@ -282,14 +362,27 @@ export function RoadmapSection() {
const isActiv = item.status === 'activ';
const isDone = item.status === 'done';

const scale = itemScales.get(index) ?? 1.0;

return (
<AnimatedElement
key={`${item.date}-${item.title}`}
types={['fadeIn']}
triggerImmediately={false}
>
<div
className={`single-timeline flex items-center mb-[22px] ${isEven ? 'flex-row-reverse' : ''}`}
ref={(el) => {
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',
}}
>
<div className="timeline-blank w-1/2"></div>
<div
Expand Down Expand Up @@ -339,7 +432,7 @@ export function RoadmapSection() {
{item.title}
</h6>
)}
{item.description && <span> — {item.description}</span>}
{item.description && <span> — {item.url ? <a href={item.url} target="_blank" rel="noopener noreferrer" className="text-inherit hover:text-[var(--color1)]" title={`Visit ${item.url}`}>{item.description}</a> : item.description}</span>}
</span>
</div>
</div>
Expand Down
Loading