223 lines
6.9 KiB
TypeScript
223 lines
6.9 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import {
|
|
View,
|
|
ScrollView,
|
|
ActivityIndicator,
|
|
useColorScheme,
|
|
Share,
|
|
Platform,
|
|
} from "react-native";
|
|
import { useSirouRouter } from "@sirou/react-native";
|
|
import { AppRoutes } from "@/lib/routes";
|
|
import { Stack, useLocalSearchParams } from "expo-router";
|
|
import { Text } from "@/components/ui/text";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Clock,
|
|
Share2,
|
|
Newspaper,
|
|
Calendar,
|
|
Tag,
|
|
AlertCircle,
|
|
Eye,
|
|
} from "@/lib/icons";
|
|
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
|
import { StandardHeader } from "@/components/StandardHeader";
|
|
import { api } from "@/lib/api";
|
|
import { toast } from "@/lib/toast-store";
|
|
|
|
export default function NewsDetailScreen() {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const { id } = useLocalSearchParams();
|
|
const colorScheme = useColorScheme();
|
|
const isDark = colorScheme === "dark";
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
const [news, setNews] = useState<any>(null);
|
|
|
|
const newsId = Array.isArray(id) ? id[0] : id;
|
|
|
|
useEffect(() => {
|
|
if (newsId) {
|
|
fetchNewsDetail();
|
|
}
|
|
}, [newsId]);
|
|
|
|
const fetchNewsDetail = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const data = await api.news.getById({ params: { id: newsId } });
|
|
setNews(data);
|
|
} catch (error: any) {
|
|
console.error("[NewsDetail] Fetch error:", error);
|
|
toast.error("Error", "Failed to load news content.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleShare = async () => {
|
|
if (!news) return;
|
|
try {
|
|
await Share.share({
|
|
title: news.title,
|
|
message: `${news.title}\n\n${news.content.substring(0, 100)}...\n\nRead more on Yaltopia Tickets.`,
|
|
});
|
|
} catch (error) {
|
|
console.error("[NewsDetail] Share error:", error);
|
|
}
|
|
};
|
|
|
|
const getCategoryStyles = (category: string) => {
|
|
switch (category) {
|
|
case "ANNOUNCEMENT":
|
|
return {
|
|
bg: "bg-amber-500/10",
|
|
text: "text-amber-500",
|
|
dot: "bg-amber-500",
|
|
};
|
|
case "UPDATE":
|
|
return {
|
|
bg: "bg-blue-500/10",
|
|
text: "text-blue-500",
|
|
dot: "bg-blue-500",
|
|
};
|
|
case "MAINTENANCE":
|
|
return { bg: "bg-red-500/10", text: "text-red-500", dot: "bg-red-500" };
|
|
default:
|
|
return {
|
|
bg: "bg-emerald-500/10",
|
|
text: "text-emerald-500",
|
|
dot: "bg-emerald-500",
|
|
};
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<Stack.Screen options={{ headerShown: false }} />
|
|
<StandardHeader title="News" showBack />
|
|
<View className="flex-1 justify-center items-center">
|
|
<ActivityIndicator color="#ea580c" size="large" />
|
|
</View>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|
|
|
|
if (!news) {
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<Stack.Screen options={{ headerShown: false }} />
|
|
<StandardHeader title="News" showBack />
|
|
<View className="flex-1 justify-center items-center px-10">
|
|
<AlertCircle size={48} color="#ef4444" className="mb-4" />
|
|
<Text variant="h4" className="text-center mb-2">
|
|
Content Not Found
|
|
</Text>
|
|
<Text variant="muted" className="text-center mb-6">
|
|
This news item might have been removed or is no longer available.
|
|
</Text>
|
|
<Button className="w-full rounded-[6px]" onPress={() => nav.back()}>
|
|
<Text className="font-sans-bold uppercase tracking-widest text-xs">
|
|
Go Back
|
|
</Text>
|
|
</Button>
|
|
</View>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|
|
|
|
const styles = getCategoryStyles(news.category);
|
|
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<Stack.Screen options={{ headerShown: false }} />
|
|
<StandardHeader title="Article" showBack />
|
|
|
|
<ScrollView
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{ paddingBottom: 60 }}
|
|
>
|
|
<View className="px-5 pt-6">
|
|
{/* Metadata Row */}
|
|
<View className="flex-row items-center justify-between mb-4">
|
|
<View
|
|
className={`px-3 py-1 rounded-full flex-row items-center gap-2 ${styles.bg}`}
|
|
>
|
|
<View className={`w-2 h-2 rounded-full ${styles.dot}`} />
|
|
<Text
|
|
className={`text-[10px] font-sans-black uppercase tracking-widest ${styles.text}`}
|
|
>
|
|
{news.category}
|
|
</Text>
|
|
</View>
|
|
<View className="flex-row items-center gap-2">
|
|
<Eye size={12} color="#94a3b8" />
|
|
<Text
|
|
variant="muted"
|
|
className="text-[10px] font-sans-bold uppercase tracking-widest"
|
|
>
|
|
{news.viewCount || 0} Views
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Title */}
|
|
<Text className="text-foreground font-sans-black text-3xl leading-[36px] mb-6 tracking-tighter">
|
|
{news.title}
|
|
</Text>
|
|
|
|
{/* Author/Date Info */}
|
|
<View className="flex-row items-center gap-4 mb-1 border-y border-border/40 py-4">
|
|
<View className="flex-row items-center gap-2">
|
|
<Calendar size={14} color="#ea580c" strokeWidth={2.5} />
|
|
<Text className="text-foreground font-sans-bold text-xs">
|
|
{new Date(news.publishedAt).toLocaleDateString(undefined, {
|
|
month: "short",
|
|
day: "numeric",
|
|
year: "numeric",
|
|
})}
|
|
</Text>
|
|
</View>
|
|
<View className="w-1 h-1 rounded-full bg-border" />
|
|
<View className="flex-row items-center gap-2">
|
|
<Clock size={14} color="#ea580c" strokeWidth={2.5} />
|
|
<Text className="text-foreground font-sans-bold text-xs">
|
|
{new Date(news.publishedAt).toLocaleTimeString(undefined, {
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
})}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Content Body */}
|
|
<View className="rounded-[6px] mb-8">
|
|
<Text
|
|
variant="p"
|
|
className="text-foreground text-base leading-7 font-sans-medium"
|
|
>
|
|
{news.content}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Footer Actions */}
|
|
<View className="flex-row gap-3">
|
|
<Button
|
|
variant="default"
|
|
className="flex-1 h-12 "
|
|
onPress={handleShare}
|
|
>
|
|
<Share2 size={18} color={isDark ? "#f1f5f9" : "#0f172a"} />
|
|
<Text className="ml-2 text-foreground font-sans-bold uppercase tracking-widest text-xs">
|
|
Share Article
|
|
</Text>
|
|
</Button>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|