Files
info-target-mobile/screens/profile/ui/ManualTab.tsx
Samandar Turgunboyev ab363ca3b9 bug fixed
2026-03-02 13:22:55 +05:00

437 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useTheme } from '@/components/ThemeContext';
import { router } from 'expo-router';
import { VideoView, useVideoPlayer } from 'expo-video';
import { ArrowLeft, Play, X } from 'lucide-react-native';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
FlatList,
Image,
Pressable,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import ImageViewer from 'react-native-image-zoom-viewer';
import Modal from 'react-native-modal';
type ManualStep = {
image: any;
description?: string;
};
type Language = {
code: 'uz' | 'ru' | 'en';
label: string;
flag: string;
};
const languages: Language[] = [
{ code: 'uz', label: "O'zbekcha", flag: '🇺🇿' },
{ code: 'ru', label: 'Русский', flag: '🇷🇺' },
{ code: 'en', label: 'English', flag: '🇬🇧' },
];
export function ManualTab() {
const { isDark } = useTheme();
const { t, i18n } = useTranslation();
const [isPlaying, setIsPlaying] = useState(false);
/** 🔹 Modal states */
const [imageVisible, setImageVisible] = useState(false);
const [imageIndex, setImageIndex] = useState(0);
const [langPickerVisible, setLangPickerVisible] = useState(false);
/** 🔹 Video tili (SELECT uchun) */
const userLang = i18n.language.startsWith('ru')
? 'ru'
: i18n.language.startsWith('en')
? 'en'
: 'uz';
const [selectedLang, setSelectedLang] = useState<'uz' | 'ru' | 'en'>(userLang);
/** 🔹 RASM tili (faqat app tili boyicha) */
const imageLang: 'uz' | 'ru' | 'en' = userLang;
/** 🔹 Theme */
const theme = {
background: isDark ? '#0f172a' : '#f8fafc',
cardBg: isDark ? '#1e293b' : '#ffffff',
text: isDark ? '#ffffff' : '#0f172a',
primary: '#3b82f6',
textMuted: isDark ? '#94a3b8' : '#64748b',
border: isDark ? '#334155' : '#e2e8f0',
};
/** 🔹 Video manbalari (SELECT ga bogliq) */
const videos = {
uz: require('@/assets/manual/manual_video_uz.mp4'),
ru: require('@/assets/manual/manual_video_ru.mp4'),
en: require('@/assets/manual/manual_video_en.mp4'),
};
const player = useVideoPlayer(videos[selectedLang], (player) => {
player.loop = false;
});
/** 🔹 Rasmlar (FAqat imageLang) */
const steps: ManualStep[] = [
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step1.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step1.jpg')
: require('@/assets/manual/image_en/step1.jpg'),
description: t("Ilovani oching va ro'yxatdan o'ting"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step2.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step2.jpg')
: require('@/assets/manual/image_en/step2.jpg'),
description: t("Profilni to'ldiring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step3.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step3.jpg')
: require('@/assets/manual/image_en/step3.jpg'),
description: t("To'lov usulini qo'shing"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step4.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step4.jpg')
: require('@/assets/manual/image_en/step4.jpg'),
description: t('Xaridni tanlang'),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step5.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step5.jpg')
: require('@/assets/manual/image_en/step5.jpg'),
description: t("To'lovni amalga oshiring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step6.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step6.jpg')
: require('@/assets/manual/image_en/step6.jpg'),
description: t("Natijani ko'ring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step7.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step7.jpg')
: require('@/assets/manual/image_en/step7.jpg'),
description: t("Natijani ko'ring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step8.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step8.jpg')
: require('@/assets/manual/image_en/step8.jpg'),
description: t("Natijani ko'ring"),
},
];
/** 🔹 Image viewer */
const images = useMemo(
() => steps.map((step) => ({ url: '', props: { source: step.image } })),
[imageLang]
);
useEffect(() => {
// listener qo'shish
const subscription = player.addListener('playingChange', (state) => {
setIsPlaying(state.isPlaying);
});
return () => {
subscription.remove();
};
}, [player]);
const renderStep = ({ item, index }: { item: ManualStep; index: number }) => (
<Pressable
onPress={() => {
setImageIndex(index);
setImageVisible(true);
}}
>
<View style={[styles.stepCard, { backgroundColor: theme.cardBg }]}>
<Image source={item.image} style={styles.stepImage} />
</View>
</Pressable>
);
return (
<ScrollView style={[styles.container, { backgroundColor: theme.background }]}>
{/* HEADER */}
<View style={styles.hero}>
<View style={styles.topHeader}>
<Pressable onPress={() => router.back()}>
<ArrowLeft color={theme.text} size={24} />
</Pressable>
<Text style={[styles.headerTitle, { color: theme.text }]}>
{t("Foydalanish qo'llanmasi")}
</Text>
</View>
</View>
{/* VIDEO */}
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.text }]}>
{t("Foydalanish qo'llanmasi")}
</Text>
<Text style={[styles.subtitle, { color: theme.textMuted }]}>
{t("Quyidagi qisqa video yoki rasmlarni ko'rib chiqing")}
</Text>
{/* <View style={{ alignContent: 'flex-end', alignItems: 'flex-end', paddingHorizontal: 16 }}>
<Pressable
style={[
styles.langSelector,
{ backgroundColor: theme.cardBg, borderColor: theme.border },
]}
onPress={() => setLangPickerVisible(true)}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ fontSize: 20 }}>{selectedLanguage?.flag}</Text>
</View>
<ChevronDown color={theme.textMuted} />
</Pressable>
</View> */}
<View style={[styles.videoCard, { backgroundColor: theme.cardBg }]}>
<VideoView
player={player}
style={styles.video}
allowsFullscreen
allowsPictureInPicture
nativeControls={true}
contentFit="cover"
/>
{!isPlaying && (
<View
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
}}
>
<TouchableOpacity
style={{
backgroundColor: 'white',
borderRadius: 50,
width: 40,
height: 40,
justifyContent: 'center',
alignContent: 'center',
alignItems: 'center',
}}
onPress={() => {
player.play();
setIsPlaying(true);
}}
>
<Play color={'white'} size={26} fill={'black'} />
</TouchableOpacity>
</View>
)}
</View>
</View>
{/* RASMLAR */}
<View style={styles.section}>
{/* <Text style={[styles.sectionTitle, { color: theme.text }]}>
{t('Foydalanish rasm qollanma')}
</Text> */}
<FlatList
horizontal
data={steps}
renderItem={renderStep}
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ paddingHorizontal: 16, gap: 20 }}
/>
</View>
{/* IMAGE MODAL */}
<Modal isVisible={imageVisible} style={{ margin: 0 }}>
<ImageViewer
imageUrls={images}
index={imageIndex}
enableSwipeDown
onSwipeDown={() => setImageVisible(false)}
renderHeader={() => (
<Pressable style={styles.closeButton} onPress={() => setImageVisible(false)}>
<X size={32} color="#fff" />
</Pressable>
)}
/>
</Modal>
{/* LANGUAGE MODAL */}
{/* <Modal isVisible={langPickerVisible} onBackdropPress={() => setLangPickerVisible(false)}>
<View style={[styles.langModalContent, { backgroundColor: theme.cardBg }]}>
<Text style={{ color: 'white', marginBottom: 10, fontSize: 18 }}>
{t('Video tilini tanlang')}
</Text>
{languages.map((lang) => {
const isSelected = selectedLang === lang.code;
return (
<Pressable
key={lang.code}
onPress={() => {
setSelectedLang(lang.code);
setLangPickerVisible(false);
}}
style={[
styles.langOption,
{
borderColor: isSelected ? theme.primary : theme.border,
backgroundColor: isSelected ? theme.primary + '15' : 'transparent',
},
]}
>
<View style={styles.langOptionLeft}>
<Text style={{ fontSize: 26 }}>{lang.flag}</Text>
<Text
style={{
color: isSelected ? theme.primary : theme.text,
fontWeight: isSelected ? '700' : '500',
fontSize: 16,
}}
>
{lang.label}
</Text>
</View>
{isSelected && (
<View style={[styles.checkmark, { backgroundColor: theme.primary }]}>
<Check size={20} color={'white'} />
</View>
)}
</Pressable>
);
})}
</View>
</Modal> */}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
hero: { padding: 20 },
topHeader: { flexDirection: 'row', alignItems: 'center', gap: 12 },
headerTitle: { fontSize: 18, fontWeight: '700', marginHorizontal: 16 },
subtitle: { fontSize: 16, marginTop: 5, fontWeight: '500', marginHorizontal: 16 },
section: { marginBottom: 28 },
sectionTitle: { fontSize: 20, fontWeight: '700', marginLeft: 16 },
langSelector: {
margin: 5,
width: 80,
gap: 5,
paddingVertical: 5,
borderRadius: 14,
borderWidth: 1.5,
alignContent: 'center',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'center',
},
videoCard: {
marginHorizontal: 16,
marginTop: 10,
borderRadius: 20,
overflow: 'hidden',
position: 'relative',
},
video: { width: '100%', aspectRatio: 9 / 13 },
stepCard: { borderRadius: 16, overflow: 'hidden' },
stepImage: { width: 300, height: 300 },
closeButton: {
position: 'absolute',
top: 50,
right: 20,
padding: 8,
zIndex: 50,
backgroundColor: 'rgba(0,0,0,0.5)',
borderRadius: 20,
},
langModalContent: {
padding: 20,
borderRadius: 20,
},
langOption: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 14,
paddingHorizontal: 16,
borderRadius: 14,
borderWidth: 1.5,
marginBottom: 10,
},
langOptionLeft: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
checkmark: {
width: 24,
height: 24,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
},
checkmarkText: {
color: '#fff',
fontSize: 14,
fontWeight: 'bold',
},
playOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0,0,0,0.25)',
},
});