133 lines
5.0 KiB
TypeScript
133 lines
5.0 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { View, Text, StyleSheet, ScrollView, Platform } from 'react-native';
|
|
import { Feather } from '@expo/vector-icons';
|
|
import { useAppTheme } from '../../context/ThemeContext';
|
|
import { AppScreen } from '../../components/AppScreen';
|
|
import { AISectionHeader, AISkeleton, AIPressable } from '../../components/UI';
|
|
import { useTranslation } from '../../context/LanguageContext';
|
|
import { MOCK_NOTIFICATIONS } from '../../constants/mocks';
|
|
import { PALETTE } from '../../constants/theme';
|
|
import * as Haptics from 'expo-haptics';
|
|
|
|
const LIME = PALETTE.lime;
|
|
|
|
const TYPE_MAP: Record<string, { icon: any; color: string }> = {
|
|
success: { icon: 'check-circle', color: LIME },
|
|
info: { icon: 'info', color: '#3B82F6' },
|
|
warning: { icon: 'alert-circle', color: '#F59E0B' },
|
|
alert: { icon: 'shield', color: '#EF4444' },
|
|
update: { icon: 'refresh-cw', color: '#8B5CF6' },
|
|
};
|
|
|
|
// Mock data moved to constants/mocks.ts
|
|
|
|
export default function NotificationsScreen() {
|
|
const { colors, isDark } = useAppTheme();
|
|
const { t } = useTranslation();
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => setLoading(false), 1500);
|
|
return () => clearTimeout(timer);
|
|
}, []);
|
|
|
|
const cardBg = colors.surface;
|
|
const border = colors.border;
|
|
const subText = colors.textSecondary;
|
|
|
|
const renderSkeleton = () => (
|
|
<View style={{ paddingHorizontal: 24, paddingTop: 56 }}>
|
|
<AISkeleton width={140} height={32} style={{ marginBottom: 10 }} />
|
|
<AISkeleton width={180} height={14} style={{ marginBottom: 32 }} />
|
|
{[1, 2, 3, 4].map(i => (
|
|
<View key={i} style={{ flexDirection: 'row', marginBottom: 12, gap: 14 }}>
|
|
<AISkeleton width={48} height={48} radius={14} />
|
|
<View style={{ flex: 1, justifyContent: 'center' }}>
|
|
<AISkeleton width="70%" height={14} style={{ marginBottom: 8 }} />
|
|
<AISkeleton width="40%" height={10} />
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
);
|
|
|
|
if (loading) return <AppScreen scrollable={false}>{renderSkeleton()}</AppScreen>;
|
|
|
|
return (
|
|
<AppScreen>
|
|
<View>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<Text style={[styles.title, { color: colors.text }]}>{t.notifications || 'Activity'}</Text>
|
|
<Text style={[styles.subtitle, { color: subText }]}>
|
|
{MOCK_NOTIFICATIONS.length} {t.recentNotifications || 'recent notifications'}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* List */}
|
|
<View style={styles.list}>
|
|
{MOCK_NOTIFICATIONS.map((item, index) => {
|
|
const meta = TYPE_MAP[item.type] || TYPE_MAP.info;
|
|
return (
|
|
<AIPressable
|
|
key={item.id}
|
|
onPress={() => {
|
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
}}
|
|
style={styles.notifPressable}
|
|
>
|
|
<View style={[styles.card, { backgroundColor: cardBg, borderColor: border }]}>
|
|
{/* Left: colored icon */}
|
|
<View style={[styles.iconBox, { backgroundColor: `${meta.color}18` }]}>
|
|
<Feather name={meta.icon} size={22} color={meta.color} />
|
|
</View>
|
|
|
|
{/* Body */}
|
|
<View style={styles.body}>
|
|
<View style={styles.topRow}>
|
|
<Text style={[styles.notifTitle, { color: colors.text }]} numberOfLines={1}>
|
|
{item.title}
|
|
</Text>
|
|
<Text style={[styles.time, { color: subText }]}>{item.time}</Text>
|
|
</View>
|
|
<Text style={[styles.desc, { color: subText }]} numberOfLines={2}>
|
|
{item.desc}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</AIPressable>
|
|
);
|
|
})}
|
|
</View>
|
|
<View style={{ height: 110 }} />
|
|
</View>
|
|
</AppScreen>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
header: { paddingHorizontal: 24, paddingTop: 20, marginBottom: 22 },
|
|
title: { fontSize: 32, fontFamily: 'Outfit_800ExtraBold', letterSpacing: -0.5 },
|
|
subtitle: { fontSize: 13, fontFamily: 'Outfit_400Regular', marginTop: 4 },
|
|
|
|
list: { paddingHorizontal: 24 },
|
|
notifPressable: { marginBottom: 10 },
|
|
card: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
padding: 16,
|
|
borderRadius: 20,
|
|
borderWidth: 1,
|
|
},
|
|
iconBox: {
|
|
width: 48, height: 48, borderRadius: 14,
|
|
alignItems: 'center', justifyContent: 'center',
|
|
marginRight: 14, flexShrink: 0,
|
|
},
|
|
body: { flex: 1 },
|
|
topRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 4 },
|
|
notifTitle: { fontSize: 15, fontFamily: 'Outfit_700Bold', flex: 1, marginRight: 8 },
|
|
time: { fontSize: 11, fontFamily: 'Outfit_500Medium', flexShrink: 0, marginTop: 1 },
|
|
desc: { fontSize: 13, fontFamily: 'Outfit_400Regular', lineHeight: 18 },
|
|
});
|