Yaltopia-Tickets-App/app/news/[id].tsx
2026-05-21 16:13:16 +03:00

223 lines
6.8 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-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-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-bold uppercase tracking-widest"
>
{news.viewCount || 0} Views
</Text>
</View>
</View>
{/* Title */}
<Text className="text-foreground font-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-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-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-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-bold uppercase tracking-widest text-xs">
Share Article
</Text>
</Button>
</View>
</View>
</ScrollView>
</ScreenWrapper>
);
}