api: fetch shows implemented
This commit is contained in:
56
apis/showApi.ts
Normal file
56
apis/showApi.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
export type RawShow = {
|
||||||
|
showId: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
genre: string;
|
||||||
|
thumbnailUrl: string;
|
||||||
|
bannerUrl?: string;
|
||||||
|
concept: string;
|
||||||
|
streamingServices: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Show = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
genres: string[];
|
||||||
|
thumbnailUri: string;
|
||||||
|
bannerUri: string;
|
||||||
|
streamingService: string;
|
||||||
|
concept: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const API_URL = "http://45.157.177.99:8080/shows";
|
||||||
|
|
||||||
|
export async function getShows(): Promise<Show[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(API_URL);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
const data: unknown = await response.json();
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
console.warn("Expected array, got:", data);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return (data as RawShow[]).map((s) => ({
|
||||||
|
id: s.showId,
|
||||||
|
title: s.title,
|
||||||
|
description: s.description,
|
||||||
|
genres: s.genre ? s.genre.split(",").map((g) => g.trim()) : [],
|
||||||
|
thumbnailUri: s.thumbnailUrl,
|
||||||
|
bannerUri: s.bannerUrl ?? "",
|
||||||
|
streamingService: s.streamingServices,
|
||||||
|
concept: s.concept,
|
||||||
|
startDate: s.startDate,
|
||||||
|
endDate: s.endDate ?? null,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,44 @@
|
|||||||
import styles from "@/app/styles/indexStyles";
|
import styles from "@/app/styles/indexStyles";
|
||||||
import ShowCard from "@/components/ui/ShowCard";
|
import ShowCard from "@/components/ui/ShowCard";
|
||||||
|
import { useShowContext } from "@/contexts/ShowContext";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Text, View } from "react-native";
|
import { Text, View } from "react-native";
|
||||||
|
import {
|
||||||
|
GestureHandlerRootView,
|
||||||
|
ScrollView,
|
||||||
|
} from "react-native-gesture-handler";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
return (
|
const { shows } = useShowContext();
|
||||||
<View style={styles.mainContainer}>
|
|
||||||
<View style={styles.header}>
|
|
||||||
<Text style={styles.title}>FLTR</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<ShowCard
|
return (
|
||||||
onPress={() => router.push("/showDetails")}
|
<GestureHandlerRootView>
|
||||||
imageUri="https://streamcoimg-a.akamaihd.net/000/123/147/123147-Banner-L2-b81d3e6b4df34af2887f701eec382f87.jpg"
|
<View style={styles.mainContainer}>
|
||||||
streamingServiceUri="https://play-lh.googleusercontent.com/e8u4F0ED6hDMzmjg5cV_C5Sxrzr3xECniwKCD2Q8QfUeVMVRLG41TrsnqroTE7uxk4E=w240-h480-rw"
|
<View style={styles.header}>
|
||||||
liveBadgeText="LIVE"
|
<Text style={styles.title}>FLTR</Text>
|
||||||
liveBadgeContainerStyle={styles.liveBadgeContainer}
|
</View>
|
||||||
genres={["Reality", "Dating"]}
|
<ScrollView contentContainerStyle={{ paddingBottom: 30 }}>
|
||||||
/>
|
{shows.map((show) => {
|
||||||
</View>
|
const showLiveBadge = show.endDate === null;
|
||||||
|
return (
|
||||||
|
<ShowCard
|
||||||
|
key={show.id}
|
||||||
|
onPress={() => router.push("/showDetails")}
|
||||||
|
imageUri={show.bannerUri || show.thumbnailUri}
|
||||||
|
streamingServiceUri={show.streamingService}
|
||||||
|
genres={show.genres}
|
||||||
|
{...(showLiveBadge
|
||||||
|
? {
|
||||||
|
liveBadgeText: "LIVE",
|
||||||
|
liveBadgeContainerStyle: styles.liveBadgeContainer,
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</GestureHandlerRootView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
|
import { ShowProvider } from "@/contexts/ShowContext";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
import "react-native-reanimated";
|
import "react-native-reanimated";
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<ShowProvider>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack>
|
||||||
<Stack.Screen
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
name="showDetails"
|
<Stack.Screen
|
||||||
options={{
|
name="showDetails"
|
||||||
headerShown: false,
|
options={{
|
||||||
}}
|
headerShown: false,
|
||||||
/>
|
}}
|
||||||
</Stack>
|
/>
|
||||||
|
</Stack>
|
||||||
|
</ShowProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|||||||
type ShowCardProps = {
|
type ShowCardProps = {
|
||||||
imageUri: string;
|
imageUri: string;
|
||||||
streamingServiceUri: string;
|
streamingServiceUri: string;
|
||||||
liveBadgeText: string;
|
liveBadgeText?: string;
|
||||||
liveBadgeContainerStyle?: object;
|
liveBadgeContainerStyle?: object;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
@@ -33,15 +33,16 @@ const ShowCard = ({
|
|||||||
<View style={styles.streamingServiceIcon}>
|
<View style={styles.streamingServiceIcon}>
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
uri: streamingServiceUri,
|
uri: "https://play-lh.googleusercontent.com/e8u4F0ED6hDMzmjg5cV_C5Sxrzr3xECniwKCD2Q8QfUeVMVRLG41TrsnqroTE7uxk4E",
|
||||||
}}
|
}}
|
||||||
style={[StyleSheet.absoluteFillObject, { borderRadius: 15 }]}
|
style={[StyleSheet.absoluteFillObject, { borderRadius: 15 }]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
{liveBadgeText && (
|
||||||
<View style={liveBadgeContainerStyle}>
|
<View style={liveBadgeContainerStyle}>
|
||||||
<Text style={styles.liveBadgeText}>{liveBadgeText}</Text>
|
<Text style={styles.liveBadgeText}>{liveBadgeText}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
<View style={styles.genreSection}>
|
<View style={styles.genreSection}>
|
||||||
{genres.map((genre) => (
|
{genres.map((genre) => (
|
||||||
<Text key={genre} style={styles.genreLabel}>
|
<Text key={genre} style={styles.genreLabel}>
|
||||||
@@ -112,7 +113,7 @@ const styles = StyleSheet.create({
|
|||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
fontStyle: "italic",
|
fontStyle: "italic",
|
||||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
backgroundColor: "rgba(255, 255, 255, 1)",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
42
contexts/ShowContext.tsx
Normal file
42
contexts/ShowContext.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { getShows, Show } from "@/apis/showApi";
|
||||||
|
import { createContext, useContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
type ShowContextType = {
|
||||||
|
shows: Show[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShowContext = createContext<ShowContextType | null>(null);
|
||||||
|
|
||||||
|
export const ShowProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const [shows, setShows] = useState<Show[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const data = await getShows();
|
||||||
|
setShows(data);
|
||||||
|
} catch {
|
||||||
|
setError("Failed to fetch shows");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ShowContext.Provider value={{ shows, loading, error }}>
|
||||||
|
{children}
|
||||||
|
</ShowContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useShowContext = () => {
|
||||||
|
const ctx = useContext(ShowContext);
|
||||||
|
if (!ctx)
|
||||||
|
throw new Error("useShowContext must be used within a ShowProvider");
|
||||||
|
return ctx;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user