feat: 首页更新

This commit is contained in:
2025-11-13 16:47:10 +08:00
parent 9ef9233797
commit 54bf84b19b
1244 changed files with 3507 additions and 951 deletions

View File

@@ -0,0 +1,296 @@
/**
* 左侧菜单组件
* 基于 react-native-paper Drawer 组件重建
* 功能包括:
* - 用户钱包信息
* - 菜单项导航
* - 利息宝显示
* - 登录状态判断
*/
import React, { useState, useCallback, useMemo } from 'react';
import { View, Text, ScrollView, Modal, TouchableOpacity, Dimensions } from 'react-native';
import { Drawer, Button, Divider } from 'react-native-paper';
import { useColorScheme, useThemeColors } from '@/theme';
import { createThemeStyles } from '@/theme';
import { useIsLoggedIn, useUser } from '@/stores';
import Ionicons from '@expo/vector-icons/Ionicons';
import WalletAmount from '../WalletAmount';
const { width } = Dimensions.get('window');
/**
* 创建主题样式
*/
const styles = createThemeStyles((colors) => ({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
drawerContainer: {
flex: 1,
backgroundColor: colors.background,
width: width * 0.65,
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
container: {
flex: 1,
backgroundColor: colors.background,
},
walletSection: {
paddingHorizontal: 16,
paddingVertical: 16,
backgroundColor: colors.card,
marginHorizontal: 12,
marginVertical: 12,
borderRadius: 12,
},
walletLabel: {
fontSize: 12,
color: colors.text + '80',
marginBottom: 8,
},
walletButtons: {
flexDirection: 'row',
gap: 8,
marginTop: 12,
},
buttonText: {
fontSize: 12,
fontWeight: '600',
},
lixibaoSection: {
marginHorizontal: 12,
marginVertical: 12,
paddingHorizontal: 12,
paddingVertical: 12,
borderRadius: 12,
borderWidth: 2,
borderColor: colors.primary,
backgroundColor: colors.card,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
lixibaoLeft: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
lixibaoInfo: {
flex: 1,
},
lixibaoLabel: {
fontSize: 12,
color: colors.text + '80',
},
lixibaoValue: {
fontSize: 14,
fontWeight: '700',
color: colors.primary,
marginTop: 4,
},
emptyText: {
textAlign: 'center',
fontSize: 14,
color: colors.text + '80',
paddingVertical: 40,
},
}));
interface LeftMenuProps {
visible: boolean;
onClose: () => void;
}
interface MenuItem {
id: string;
title: string;
icon: string;
route?: string;
requireLogin?: boolean;
}
/**
* 左侧菜单组件
*/
export default function LeftMenu({ visible, onClose }: LeftMenuProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const themeColors = useThemeColors();
const isLoggedIn = useIsLoggedIn();
const user = useUser();
const [walletBalance] = useState(0); // 从 store 获取
const [interestBalance] = useState(0); // 从 store 获取
// 菜单项配置
const menuItems: MenuItem[] = useMemo(
() => [
{ id: 'balance', title: '利息宝', icon: 'gift' },
{
id: 'services',
title: '客服',
icon: 'headset',
route: '/services',
},
{
id: 'collect',
title: '收藏',
icon: 'star',
route: '/collect',
},
{
id: 'recent',
title: '最近',
icon: 'time',
route: '/recent',
},
{
id: 'betRecord',
title: '投注记录',
icon: 'document-text',
route: '/betRecord',
requireLogin: true,
},
{
id: 'activity',
title: '任务中心',
icon: 'checkmark-circle',
route: '/activity',
},
{
id: 'bonus',
title: '福利中心',
icon: 'gift',
route: '/bonus',
},
{
id: 'rebate',
title: '自助返水',
icon: 'cash',
route: '/rebate',
},
{
id: 'findBalance',
title: '找回余额',
icon: 'search',
route: '/findBalance',
},
{
id: 'message',
title: '消息中心',
icon: 'mail',
route: '/message',
},
{
id: 'promotion',
title: '推广赚钱',
icon: 'share-social',
route: '/promotion',
},
{
id: 'backup',
title: '备用网站',
icon: 'link',
route: '/backup',
},
],
[]
);
const handleMenuItemPress = useCallback(
(item: MenuItem) => {
if (item.requireLogin && !isLoggedIn) {
// 显示登录提示
return;
}
// 导航到对应页面
console.log('Navigate to:', item.route);
onClose();
},
[isLoggedIn, onClose]
);
const handleDeposit = useCallback(() => {
console.log('Navigate to deposit');
onClose();
}, [onClose]);
const handleWithdraw = useCallback(() => {
console.log('Navigate to withdraw');
onClose();
}, [onClose]);
return (
<Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
<View style={s.overlay}>
<TouchableOpacity style={{ flex: 1 }} activeOpacity={1} onPress={onClose} />
<View style={s.drawerContainer}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* 用户信息或登录提示 */}
{isLoggedIn && user ? (
<>
{/* 钱包信息 */}
<View style={s.walletSection}>
<Text style={s.walletLabel}></Text>
<WalletAmount balance={walletBalance} />
<View style={s.walletButtons}>
<Button
mode="contained"
onPress={handleDeposit}
style={{ flex: 1 }}
labelStyle={s.buttonText}
>
</Button>
<Button
mode="outlined"
onPress={handleWithdraw}
style={{ flex: 1 }}
labelStyle={s.buttonText}
>
</Button>
</View>
</View>
</>
) : (
<Text style={s.emptyText}></Text>
)}
<Divider style={{ marginVertical: 8 }} />
{/* 菜单项 */}
<Drawer.Section title="">
{menuItems.map((item) => (
<Drawer.Item
key={item.id}
label={item.title}
icon={({ color, size }) => (
<Ionicons name={item.icon as any} size={size} color={color} />
)}
onPress={() => handleMenuItemPress(item)}
active={false}
right={() => (
<Ionicons
name="chevron-forward"
size={20}
color={themeColors.text + '60'}
style={{ marginRight: -10 }}
/>
)}
style={{ marginHorizontal: 0 }}
/>
))}
</Drawer.Section>
</ScrollView>
</View>
</View>
</Modal>
);
}

View File

@@ -0,0 +1,363 @@
/**
* 登录/注册弹窗组件
* 支持登录和注册两种模式
*/
import React, { useState, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TextInput,
Modal,
ScrollView,
KeyboardAvoidingView,
Platform,
Alert,
} from 'react-native';
import { useColorScheme, useThemeColors } from '@/theme';
import { createThemeStyles } from '@/theme';
import { useUserStore } from '@/stores';
import Ionicons from '@expo/vector-icons/Ionicons';
/**
* 创建主题样式
*/
const styles = createThemeStyles((colors) => ({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'flex-end',
},
container: {
backgroundColor: colors.background,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 40,
maxHeight: '80%',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
title: {
fontSize: 18,
fontWeight: '700',
color: colors.text,
},
closeButton: {
padding: 8,
},
tabContainer: {
flexDirection: 'row',
marginBottom: 20,
borderBottomWidth: 1,
borderBottomColor: colors.borderSecondary,
},
tab: {
flex: 1,
paddingVertical: 12,
alignItems: 'center',
borderBottomWidth: 2,
borderBottomColor: 'transparent',
},
activeTab: {
borderBottomColor: colors.primary,
},
tabText: {
fontSize: 14,
fontWeight: '600',
color: colors.text + '80',
},
activeTabText: {
color: colors.primary,
},
form: {
gap: 12,
},
inputContainer: {
marginBottom: 12,
},
label: {
fontSize: 12,
color: colors.text + '80',
marginBottom: 6,
},
input: {
borderWidth: 1,
borderColor: colors.borderSecondary,
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
fontSize: 14,
color: colors.text,
backgroundColor: colors.card,
},
inputFocused: {
borderColor: colors.primary,
},
errorText: {
fontSize: 12,
color: '#ff4444',
marginTop: 4,
},
submitButton: {
backgroundColor: colors.primary,
borderRadius: 8,
paddingVertical: 12,
alignItems: 'center',
marginTop: 20,
},
submitButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '700',
},
divider: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 16,
},
dividerLine: {
flex: 1,
height: 1,
backgroundColor: colors.borderSecondary,
},
dividerText: {
marginHorizontal: 12,
fontSize: 12,
color: colors.text + '80',
},
socialButtons: {
flexDirection: 'row',
gap: 12,
},
socialButton: {
flex: 1,
borderWidth: 1,
borderColor: colors.borderSecondary,
borderRadius: 8,
paddingVertical: 10,
alignItems: 'center',
},
agreementText: {
fontSize: 12,
color: colors.text + '80',
marginTop: 12,
lineHeight: 18,
},
agreementLink: {
color: colors.primary,
textDecorationLine: 'underline',
},
}));
interface LoginRegisterModalProps {
visible: boolean;
onClose: () => void;
}
/**
* 登录/注册弹窗组件
*/
export default function LoginRegisterModal({ visible, onClose }: LoginRegisterModalProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const themeColors = useThemeColors();
const { login } = useUserStore();
const [mode, setMode] = useState<'login' | 'register'>('login');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [errors, setErrors] = useState<Record<string, string>>({});
const [loading, setLoading] = useState(false);
const validateEmail = useCallback((email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}, []);
const validateForm = useCallback(() => {
const newErrors: Record<string, string> = {};
if (!email.trim()) {
newErrors.email = '请输入邮箱';
} else if (!validateEmail(email)) {
newErrors.email = '邮箱格式不正确';
}
if (!password.trim()) {
newErrors.password = '请输入密码';
} else if (password.length < 6) {
newErrors.password = '密码至少6个字符';
}
if (mode === 'register') {
if (!confirmPassword.trim()) {
newErrors.confirmPassword = '请确认密码';
} else if (password !== confirmPassword) {
newErrors.confirmPassword = '两次输入的密码不一致';
}
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}, [email, password, confirmPassword, mode, validateEmail]);
const handleSubmit = useCallback(async () => {
if (!validateForm()) {
return;
}
try {
setLoading(true);
// 模拟 API 调用
await new Promise((resolve) => setTimeout(resolve, 1000));
// 模拟登录/注册成功
const mockUser = {
id: '1',
username: email.split('@')[0],
email,
avatar: 'https://i.pravatar.cc/150?img=1',
nickname: '用户',
createdAt: new Date().toISOString(),
};
login(mockUser, 'mock-token-' + Date.now());
Alert.alert('成功', mode === 'login' ? '登录成功!' : '注册成功!');
onClose();
} catch (error: any) {
Alert.alert('失败', error.message || '操作失败,请重试');
} finally {
setLoading(false);
}
}, [validateForm, email, mode, login, onClose]);
const handleSwitchMode = useCallback(() => {
setMode(mode === 'login' ? 'register' : 'login');
setErrors({});
}, [mode]);
return (
<Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
<View style={s.overlay}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{ flex: 1 }}
>
<TouchableOpacity style={{ flex: 1 }} activeOpacity={1} onPress={onClose} />
<View style={s.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* 头部 */}
<View style={s.header}>
<Text style={s.title}>{mode === 'login' ? '登录' : '注册'}</Text>
<TouchableOpacity style={s.closeButton} onPress={onClose} activeOpacity={0.7}>
<Ionicons name="close" size={24} color={themeColors.text} />
</TouchableOpacity>
</View>
{/* 标签页 */}
<View style={s.tabContainer}>
<TouchableOpacity
style={[s.tab, mode === 'login' && s.activeTab]}
onPress={() => setMode('login')}
activeOpacity={0.7}
>
<Text style={[s.tabText, mode === 'login' && s.activeTabText]}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[s.tab, mode === 'register' && s.activeTab]}
onPress={() => setMode('register')}
activeOpacity={0.7}
>
<Text style={[s.tabText, mode === 'register' && s.activeTabText]}></Text>
</TouchableOpacity>
</View>
{/* 表单 */}
<View style={s.form}>
{/* 邮箱 */}
<View style={s.inputContainer}>
<Text style={s.label}></Text>
<TextInput
style={[s.input, errors.email && { borderColor: '#ff4444' }]}
placeholder="请输入邮箱"
placeholderTextColor={themeColors.text + '60'}
value={email}
onChangeText={setEmail}
keyboardType="email-address"
editable={!loading}
/>
{errors.email && <Text style={s.errorText}>{errors.email}</Text>}
</View>
{/* 密码 */}
<View style={s.inputContainer}>
<Text style={s.label}></Text>
<TextInput
style={[s.input, errors.password && { borderColor: '#ff4444' }]}
placeholder="请输入密码"
placeholderTextColor={themeColors.text + '60'}
value={password}
onChangeText={setPassword}
secureTextEntry
editable={!loading}
/>
{errors.password && <Text style={s.errorText}>{errors.password}</Text>}
</View>
{/* 确认密码(注册模式) */}
{mode === 'register' && (
<View style={s.inputContainer}>
<Text style={s.label}></Text>
<TextInput
style={[s.input, errors.confirmPassword && { borderColor: '#ff4444' }]}
placeholder="请再次输入密码"
placeholderTextColor={themeColors.text + '60'}
value={confirmPassword}
onChangeText={setConfirmPassword}
secureTextEntry
editable={!loading}
/>
{errors.confirmPassword && (
<Text style={s.errorText}>{errors.confirmPassword}</Text>
)}
</View>
)}
</View>
{/* 提交按钮 */}
<TouchableOpacity
style={s.submitButton}
onPress={handleSubmit}
disabled={loading}
activeOpacity={0.7}
>
<Text style={s.submitButtonText}>
{loading ? '处理中...' : mode === 'login' ? '登录' : '注册'}
</Text>
</TouchableOpacity>
{/* 用户协议 */}
{mode === 'register' && (
<Text style={s.agreementText}>
<Text style={s.agreementLink}></Text>
<Text style={s.agreementLink}></Text>
</Text>
)}
</ScrollView>
</View>
</KeyboardAvoidingView>
</View>
</Modal>
);
}

View File

@@ -1,9 +1,15 @@
/**
* 首页 Header 组件
* 包含搜索、用户信息、消息等功能
* Header 组件 - 增强版
* 基于 xinyong-web 的 header 组件重建
* 功能包括:
* - 顶部导航栏logo、搜索、用户按钮
* - 左侧菜单(侧边栏)
* - 登录/注册弹窗
* - 钱包信息显示
* - 主题适配
*/
import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useMemo } from 'react';
import {
View,
Text,
@@ -12,154 +18,150 @@ import {
TextInput,
Image,
Dimensions,
Modal,
ScrollView,
SafeAreaView,
} from 'react-native';
import { createThemeStyles, useColorScheme, useThemeInfo } from '@/theme';
import { useColorScheme, useThemeColors } from '@/theme';
import { useIsLoggedIn, useUser } from '@/stores/userStore';
import { useLogo } from '@/stores/tenantStore';
import { Ionicons, AntDesign } from '@expo/vector-icons';
import LeftMenu from './LeftMenu';
import LoginRegisterModal from './LoginRegisterModal';
import WalletAmount from '../WalletAmount';
import { styles } from './styles';
const { width } = Dimensions.get('window');
/**
* 创建主题样式
*/
const styles = createThemeStyles((colors) => ({
container: {
backgroundColor: colors.background,
paddingHorizontal: 12,
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 8,
},
logo: {
fontSize: 18,
fontWeight: '600',
color: colors.primary,
},
searchContainer: {
flex: 1,
marginHorizontal: 12,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.card,
borderRadius: 20,
paddingHorizontal: 12,
height: 36,
},
searchInput: {
flex: 1,
marginLeft: 8,
fontSize: 14,
color: colors.text,
},
searchPlaceholder: {
color: colors.text + '80',
},
iconButton: {
width: 36,
height: 36,
borderRadius: 18,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 8,
},
badge: {
position: 'absolute',
top: -4,
right: -4,
backgroundColor: colors.primary,
borderRadius: 8,
minWidth: 16,
height: 16,
justifyContent: 'center',
alignItems: 'center',
},
badgeText: {
color: '#fff',
fontSize: 10,
fontWeight: '600',
textAlign: 'center',
},
}));
interface HeaderProps {
onSearch?: (keyword: string) => void;
onMessagePress?: () => void;
onUserPress?: () => void;
onMenuPress?: () => void;
unreadCount?: number;
}
/**
* Header 组件
* Header 组件 - 增强版
*/
export default function Header({
onSearch,
onMessagePress,
onUserPress,
onMenuPress,
unreadCount = 0,
}: HeaderProps) {
const theme = useColorScheme();
const s = styles[theme];
const { colors } = useThemeInfo();
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const themeColors = useThemeColors();
const isLoggedIn = useIsLoggedIn();
const user = useUser();
const logoUrl = useLogo();
const [searchText, setSearchText] = useState('');
const [isSearching, setIsSearching] = useState(false);
const [showLeftMenu, setShowLeftMenu] = useState(false);
const [showLoginModal, setShowLoginModal] = useState(false);
const handleSearch = useCallback(() => {
if (searchText.trim()) {
onSearch?.(searchText);
setSearchText('');
}
}, [searchText, onSearch]);
const handleClearSearch = useCallback(() => {
setSearchText('');
const handleMenuPress = useCallback(() => {
setShowLeftMenu(true);
onMenuPress?.();
}, [onMenuPress]);
const handleLoginPress = useCallback(() => {
setShowLoginModal(true);
}, []);
return (
<View style={s.container}>
{/* 顶部栏 */}
<View style={s.header}>
{/* Logo */}
<Text style={s.logo}>🎮 </Text>
<>
<View style={s.container}>
{/* 左侧部分 */}
<View style={s.leftSection}>
{/* 菜单按钮 */}
<TouchableOpacity style={s.menuButton} onPress={handleMenuPress} activeOpacity={0.7}>
<AntDesign name="menu-unfold" size={22} color={themeColors.text} />
</TouchableOpacity>
{/* 搜索框 */}
<View style={s.searchContainer}>
<Text style={{ color: colors.text + '60', fontSize: 16 }}>🔍</Text>
<TextInput
style={[s.searchInput, s.searchPlaceholder]}
placeholder="搜索游戏..."
placeholderTextColor={colors.text + '60'}
value={searchText}
onChangeText={setSearchText}
onSubmitEditing={handleSearch}
returnKeyType="search"
/>
{searchText ? (
<TouchableOpacity onPress={handleClearSearch}>
<Text style={{ fontSize: 16 }}></Text>
</TouchableOpacity>
) : null}
{/* Logo */}
{logoUrl ? <Image source={{ uri: logoUrl }} style={s.logoImage} /> : null}
</View>
{/* 消息按钮 */}
<TouchableOpacity style={s.iconButton} onPress={onMessagePress} activeOpacity={0.7}>
<Text style={{ fontSize: 18 }}>💬</Text>
{unreadCount > 0 && (
<View style={s.badge}>
<Text style={s.badgeText}>{unreadCount > 99 ? '99+' : unreadCount}</Text>
{/* 搜索框 */}
{/*<View style={s.searchContainer}>*/}
{/* <Ionicons name="search" size={16} color={themeColors.text + '60'} />*/}
{/* <TextInput*/}
{/* style={s.searchInput}*/}
{/* placeholder="搜索游戏..."*/}
{/* placeholderTextColor={themeColors.text + '60'}*/}
{/* value={searchText}*/}
{/* onChangeText={setSearchText}*/}
{/* onSubmitEditing={handleSearch}*/}
{/* returnKeyType="search"*/}
{/* />*/}
{/* {searchText ? (*/}
{/* <TouchableOpacity onPress={() => setSearchText('')}>*/}
{/* <Ionicons name="close" size={16} color={themeColors.text} />*/}
{/* </TouchableOpacity>*/}
{/* ) : null}*/}
{/*</View>*/}
{/* 右侧部分 */}
<View style={s.rightSection}>
{isLoggedIn && user ? (
<>
{/* 消息按钮 */}
<TouchableOpacity style={s.iconButton} onPress={onMessagePress} activeOpacity={0.7}>
<Ionicons name="chatbubble-outline" size={20} color={themeColors.text} />
{unreadCount > 0 && (
<View style={s.badge}>
<Text style={s.badgeText}>{unreadCount > 99 ? '99+' : unreadCount}</Text>
</View>
)}
</TouchableOpacity>
{/* 用户头像 */}
<TouchableOpacity style={s.iconButton} activeOpacity={0.7}>
{user.avatar ? (
<Image
source={{ uri: user.avatar }}
style={{ width: 32, height: 32, borderRadius: 16 }}
/>
) : (
<Ionicons name="person-circle" size={24} color={themeColors.primary} />
)}
</TouchableOpacity>
</>
) : (
/* 登录/注册按钮 */
<View style={s.authButtons}>
<TouchableOpacity
style={[s.authButton, s.registerButton]}
onPress={() => setShowLoginModal(true)}
activeOpacity={0.7}
>
<Text style={s.registerButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[s.authButton, s.loginButton]}
onPress={() => setShowLoginModal(true)}
activeOpacity={0.7}
>
<Text style={s.loginButtonText}></Text>
</TouchableOpacity>
</View>
)}
</TouchableOpacity>
{/* 用户按钮 */}
<TouchableOpacity style={s.iconButton} onPress={onUserPress} activeOpacity={0.7}>
<Text style={{ fontSize: 18 }}>👤</Text>
</TouchableOpacity>
</View>
</View>
</View>
{/* 左侧菜单 */}
<LeftMenu visible={showLeftMenu} onClose={() => setShowLeftMenu(false)} />
{/* 登录/注册弹窗 */}
<LoginRegisterModal visible={showLoginModal} onClose={() => setShowLoginModal(false)} />
</>
);
}

116
components/Header/styles.ts Normal file
View File

@@ -0,0 +1,116 @@
import { createThemeStyles } from '@/theme';
/**
* 创建主题样式
*/
export const styles = createThemeStyles((colors) => ({
container: {
backgroundColor: colors.background,
paddingHorizontal: 12,
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: colors.borderSecondary,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
leftSection: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
menuButton: {
width: 24,
height: 40,
borderRadius: 0,
justifyContent: 'center',
alignItems: 'center',
marginRight: 6,
},
logo: {
fontSize: 16,
fontWeight: '700',
color: colors.primary,
},
logoImage: {
width: 125,
height: 33,
resizeMode: 'contain',
},
searchContainer: {
flex: 1,
marginHorizontal: 12,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.card,
borderRadius: 20,
paddingHorizontal: 12,
height: 40,
},
searchInput: {
flex: 1,
marginLeft: 8,
fontSize: 14,
color: colors.text,
},
rightSection: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
iconButton: {
width: 40,
height: 40,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
},
badge: {
position: 'absolute',
top: -4,
right: -4,
backgroundColor: colors.primary,
borderRadius: 8,
minWidth: 18,
height: 18,
justifyContent: 'center',
alignItems: 'center',
},
badgeText: {
color: '#fff',
fontSize: 10,
fontWeight: '600',
textAlign: 'center',
},
authButtons: {
flexDirection: 'row',
gap: 8,
},
authButton: {
height: 27,
paddingHorizontal: 16,
// paddingVertical: 8,
borderRadius: 6,
justifyContent: 'center',
alignItems: 'center',
},
registerButton: {
backgroundColor: colors.card,
borderWidth: 1,
borderColor: colors.tint,
},
registerButtonText: {
color: colors.tint,
fontSize: 14,
fontWeight: '600',
},
loginButton: {
backgroundColor: colors.tint,
},
loginButtonText: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
},
}));