feat: 实现目标通知功能及相关组件
- 新增目标通知功能,支持根据用户创建目标时选择的频率和开始时间自动创建本地定时推送通知 - 实现每日、每周和每月的重复类型,用户可自定义选择提醒时间和重复规则 - 集成目标通知测试组件,方便开发者测试不同类型的通知 - 更新相关文档,详细描述目标通知功能的实现和使用方法 - 优化目标页面,确保用户体验和界面一致性
This commit is contained in:
102
components/GoalCarousel.tsx
Normal file
102
components/GoalCarousel.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import React, { useRef } from 'react';
|
||||
import { Dimensions, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import { GoalCard, GoalItem } from './GoalCard';
|
||||
|
||||
interface GoalCarouselProps {
|
||||
goals: GoalItem[];
|
||||
onGoalPress?: (item: GoalItem) => void;
|
||||
}
|
||||
|
||||
const { width: screenWidth } = Dimensions.get('window');
|
||||
|
||||
export function GoalCarousel({ goals, onGoalPress }: GoalCarouselProps) {
|
||||
const theme = useColorScheme() ?? 'light';
|
||||
const colorTokens = Colors[theme];
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
|
||||
if (!goals || goals.length === 0) {
|
||||
return (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={[styles.emptyText, { color: colorTokens.textMuted }]}>
|
||||
今天暂无目标
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
snapToInterval={(screenWidth - 60) * 0.65 + 16} // 卡片宽度 + 间距
|
||||
snapToAlignment="start"
|
||||
decelerationRate="fast"
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
style={styles.scrollView}
|
||||
>
|
||||
{goals.map((item, index) => (
|
||||
<GoalCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
onPress={onGoalPress}
|
||||
/>
|
||||
))}
|
||||
{/* 占位符,确保最后一张卡片有足够的滑动空间 */}
|
||||
<View style={{ width: 20 }} />
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部指示器 */}
|
||||
{/* <View style={styles.indicatorContainer}>
|
||||
{goals.map((_, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[
|
||||
styles.indicator,
|
||||
{ backgroundColor: index === 0 ? colorTokens.primary : colorTokens.border }
|
||||
]}
|
||||
/>
|
||||
))}
|
||||
</View> */}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
},
|
||||
scrollView: {
|
||||
},
|
||||
scrollContent: {
|
||||
paddingHorizontal: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
emptyContainer: {
|
||||
height: 140,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginHorizontal: 20,
|
||||
borderRadius: 20,
|
||||
borderWidth: 1,
|
||||
borderColor: '#E5E7EB',
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
emptyText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
indicatorContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
},
|
||||
indicator: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user