'init'
This commit is contained in:
160
app/views/Introduction/Introduction.tsx
Normal file
160
app/views/Introduction/Introduction.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user