160 lines
4.2 KiB
TypeScript
160 lines
4.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Button, Group, Text, Flex } from '@mantine/core';
|
|
import { IconShieldLock, IconBolt, IconDeviceMobile } from '@tabler/icons-react';
|
|
import classes from './Introduction.module.css';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import useWindow, { ElectronTheme } from '@/app/hooks/useWindow';
|
|
import { AnimatedButton } from '@/app/components/AnimatedButton/AnimatedButton';
|
|
|
|
const slides = [
|
|
{
|
|
title: 'Security',
|
|
description: 'Your data is protected by modern encryption and stays only on your device',
|
|
icon: IconShieldLock,
|
|
color: '#4CAF50',
|
|
},
|
|
{
|
|
title: 'Speed',
|
|
description: 'Instant synchronization and fast performance without delays',
|
|
icon: IconBolt,
|
|
color: '#FF9800',
|
|
},
|
|
{
|
|
title: 'Simple Interface',
|
|
description: 'Intuitive design that is clear at first glance',
|
|
icon: IconDeviceMobile,
|
|
color: '#2196F3',
|
|
},
|
|
];
|
|
|
|
export function Introduction() {
|
|
const navigate = useNavigate();
|
|
const {setSize, setResizeble, setTheme} = useWindow();
|
|
const [activeSlide, setActiveSlide] = useState(0);
|
|
const [touchStart, setTouchStart] = useState<number | null>(null);
|
|
const [touchEnd, setTouchEnd] = useState<number | null>(null);
|
|
|
|
// Минимальное расстояние свайпа (в px)
|
|
const minSwipeDistance = 50;
|
|
|
|
useEffect(() => {
|
|
setSize(385, 555);
|
|
setResizeble(false);
|
|
setTheme(ElectronTheme.SYSTEM);
|
|
}, []);
|
|
|
|
const handleGetStarted = () => {
|
|
navigate('/create-seed');
|
|
};
|
|
|
|
const goToSlide = (index: number) => {
|
|
setActiveSlide(index);
|
|
};
|
|
|
|
const nextSlide = () => {
|
|
setActiveSlide((prev) => (prev + 1) % slides.length);
|
|
};
|
|
|
|
const prevSlide = () => {
|
|
setActiveSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
|
};
|
|
|
|
const onTouchStart = (e: React.TouchEvent) => {
|
|
setTouchEnd(null);
|
|
setTouchStart(e.targetTouches[0].clientX);
|
|
};
|
|
|
|
const onTouchMove = (e: React.TouchEvent) => {
|
|
setTouchEnd(e.targetTouches[0].clientX);
|
|
};
|
|
|
|
const onTouchEnd = () => {
|
|
if (!touchStart || !touchEnd) return;
|
|
|
|
const distance = touchStart - touchEnd;
|
|
const isLeftSwipe = distance > minSwipeDistance;
|
|
const isRightSwipe = distance < -minSwipeDistance;
|
|
|
|
if (isLeftSwipe) {
|
|
nextSlide();
|
|
} else if (isRightSwipe) {
|
|
prevSlide();
|
|
}
|
|
};
|
|
|
|
const handleCarouselClick = () => {
|
|
nextSlide();
|
|
};
|
|
|
|
const CurrentIcon = slides[activeSlide].icon;
|
|
|
|
return (
|
|
<div className={classes.wrapper}>
|
|
<Flex h={'100%'} w={'100%'} direction={'column'} justify={'space-between'} align={'center'}>
|
|
<div>
|
|
<div
|
|
className={classes.carousel}
|
|
onTouchStart={onTouchStart}
|
|
onTouchMove={onTouchMove}
|
|
onTouchEnd={onTouchEnd}
|
|
onClick={handleCarouselClick}
|
|
style={{ cursor: 'pointer', userSelect: 'none' }}
|
|
>
|
|
<div className={classes.slideContent} key={activeSlide}>
|
|
<div
|
|
className={classes.iconWrapper}
|
|
style={{ backgroundColor: `${slides[activeSlide].color}20` }}
|
|
>
|
|
<CurrentIcon
|
|
size={80}
|
|
stroke={1.5}
|
|
color={slides[activeSlide].color}
|
|
/>
|
|
</div>
|
|
|
|
<Text size={'sm'} className={classes.title} fw={500}>
|
|
{slides[activeSlide].title}
|
|
</Text>
|
|
|
|
<Text mt={'xs'} size={'xs'} c={'dimmed'} className={classes.description}>
|
|
{slides[activeSlide].description}
|
|
</Text>
|
|
</div>
|
|
|
|
<Group justify="center" className={classes.dots}>
|
|
{slides.map((_, index) => (
|
|
<button
|
|
key={index}
|
|
className={`${classes.dot} ${index === activeSlide ? classes.dotActive : ''}`}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
goToSlide(index);
|
|
}}
|
|
aria-label={`Go to slide ${index + 1}`}
|
|
/>
|
|
))}
|
|
</Group>
|
|
</div>
|
|
</div>
|
|
<Flex gap={'xs'} direction={'column'} w={'100%'}>
|
|
<AnimatedButton
|
|
fullWidth
|
|
size="md"
|
|
animated={['#2DA5FF', '#87DBFF']}
|
|
onClick={handleGetStarted}
|
|
>
|
|
Get Started
|
|
</AnimatedButton>
|
|
<Button
|
|
fullWidth
|
|
variant={'transparent'}
|
|
size="md"
|
|
onClick={() => navigate('/exists-seed')}
|
|
>
|
|
Already have an account
|
|
</Button>
|
|
</Flex>
|
|
</Flex>
|
|
</div>
|
|
);
|
|
} |