Files
jungwoo choi 919afe56f2 feat: SAPIENS Mobile App - Initial commit
React Native mobile application for SAPIENS news platform.
Consolidated all previous history into single commit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 14:30:25 +09:00

139 lines
2.9 KiB
TypeScript

import { View, Text, TouchableOpacity, Image, StyleSheet } from 'react-native';
interface ArticleCardProps {
id: string;
title: string;
summary: string;
thumbnail: string;
publishedAt: Date;
timeAgo?: string;
outletName: string;
category: string;
onClick: (id: string) => void;
className?: string;
isAd?: boolean;
tags?: string[];
index?: number;
}
export default function ArticleCard({
id,
title,
summary,
thumbnail,
timeAgo,
onClick,
isAd = false,
index = 0,
}: ArticleCardProps) {
// Generate stable random data based on index and id
const hash = id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
const baseMinutes = index * 5;
const randomOffset = (hash % 5) + 1;
const minutesAgo = baseMinutes + randomOffset;
return (
<TouchableOpacity
style={styles.card}
onPress={() => onClick(id)}
activeOpacity={0.7}
>
{/* Thumbnail */}
<View style={styles.thumbnailContainer}>
<Image
source={{ uri: thumbnail }}
style={styles.thumbnail}
defaultSource={require('../assets/images/partial-react-logo.png')}
/>
{isAd && (
<View style={styles.adBadge}>
<Text style={styles.adText}>Ad</Text>
</View>
)}
{!isAd && (
<View style={styles.timeBadge}>
<Text style={styles.timeText}>
{timeAgo || `${minutesAgo} min ago`}
</Text>
</View>
)}
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.title} numberOfLines={2}>
{title}
</Text>
<Text style={styles.summary} numberOfLines={2}>
{summary}
</Text>
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: '#fff',
borderRadius: 12,
marginBottom: 12,
overflow: 'hidden',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
thumbnailContainer: {
width: '100%',
aspectRatio: 21 / 9,
position: 'relative',
},
thumbnail: {
width: '100%',
height: '100%',
},
adBadge: {
position: 'absolute',
top: 8,
left: 8,
backgroundColor: '#f59e0b',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
},
adText: {
color: '#fff',
fontSize: 10,
fontWeight: '600',
},
timeBadge: {
position: 'absolute',
bottom: 8,
left: 8,
backgroundColor: 'rgba(0, 0, 0, 0.6)',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
},
timeText: {
color: '#fff',
fontSize: 10,
},
content: {
padding: 12,
},
title: {
fontSize: 16,
fontWeight: '600',
color: '#000',
marginBottom: 6,
lineHeight: 22,
},
summary: {
fontSize: 14,
color: '#666',
lineHeight: 20,
},
});