filter, haptik, background
This commit is contained in:
@@ -2,6 +2,7 @@ import styles from "@/app/tabStyles/indexStyles";
|
|||||||
import ShowCard from "@/components/ui/ShowCard";
|
import ShowCard from "@/components/ui/ShowCard";
|
||||||
import { useShowContext } from "@/contexts/ShowContext";
|
import { useShowContext } from "@/contexts/ShowContext";
|
||||||
import { useStreamingServiceContext } from "@/contexts/StreamingServiceContext";
|
import { useStreamingServiceContext } from "@/contexts/StreamingServiceContext";
|
||||||
|
import * as Haptics from 'expo-haptics';
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
@@ -22,12 +23,19 @@ export default function HomeScreen() {
|
|||||||
const [filteredShows, setFilteredShows] = React.useState(shows);
|
const [filteredShows, setFilteredShows] = React.useState(shows);
|
||||||
const [activeFilter, setActiveFilter] = React.useState<string>("all");
|
const [activeFilter, setActiveFilter] = React.useState<string>("all");
|
||||||
|
|
||||||
|
const haptikFeedback = () => {
|
||||||
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setFilteredShows(shows);
|
setFilteredShows(shows);
|
||||||
}, [shows]);
|
}, [shows]);
|
||||||
|
|
||||||
const handleFilter = (type: string) => {
|
const handleFilter = (type: string) => {
|
||||||
|
haptikFeedback();
|
||||||
setActiveFilter(type);
|
setActiveFilter(type);
|
||||||
|
|
||||||
|
|
||||||
if (type === "all") {
|
if (type === "all") {
|
||||||
setFilteredShows(shows);
|
setFilteredShows(shows);
|
||||||
return;
|
return;
|
||||||
@@ -39,12 +47,26 @@ export default function HomeScreen() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filtered = shows.filter((show) => show.streamingService === type);
|
if (type === activeFilter) {
|
||||||
|
setFilteredShows(shows);
|
||||||
|
setActiveFilter('all');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const filtered = shows.filter((show) =>
|
||||||
|
show.streamingService.split(',').map(s => s.trim()).includes(type)
|
||||||
|
);
|
||||||
setFilteredShows(filtered);
|
setFilteredShows(filtered);
|
||||||
};
|
};
|
||||||
|
|
||||||
const uniqueStreamingServices = React.useMemo(() => {
|
const uniqueStreamingServices = React.useMemo(() => {
|
||||||
const uniqueServices = new Set(shows.map((show) => show.streamingService));
|
const uniqueServices = new Set<string>();
|
||||||
|
shows.forEach((show) => {
|
||||||
|
const services = show.streamingService.split(', ').map(s => s.trim());
|
||||||
|
services.forEach(service => uniqueServices.add(service));
|
||||||
|
});
|
||||||
return Array.from(uniqueServices);
|
return Array.from(uniqueServices);
|
||||||
}, [shows]);
|
}, [shows]);
|
||||||
|
|
||||||
@@ -95,36 +117,47 @@ export default function HomeScreen() {
|
|||||||
{activeFilter !== "all" && (
|
{activeFilter !== "all" && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{
|
style={{
|
||||||
padding: 2,
|
padding: 5,
|
||||||
height: 50,
|
height: 60,
|
||||||
width: 50,
|
width: 60,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
backgroundColor: "hsl(221, 39%, 80%)",
|
backgroundColor: "hsla(0, 0%, 29%, 1.00)",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
}}
|
}}
|
||||||
onPress={() => handleFilter("all")}
|
onPress={() => handleFilter("all")}
|
||||||
>
|
>
|
||||||
<Text>ALLE</Text>
|
<Text style={{fontWeight: 'bold', color: 'white'}}>ALLE</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{activeFilter !== "live" && (
|
{activeFilter !== "live" && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{
|
style={{
|
||||||
padding: 2,
|
padding: 5,
|
||||||
height: 50,
|
height: 60,
|
||||||
width: 50,
|
width: 60,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
backgroundColor: "hsl(221, 39%, 80%)",
|
backgroundColor: "hsla(0, 0%, 29%, 1.00)",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
}}
|
}}
|
||||||
onPress={() => handleFilter("live")}
|
onPress={() => handleFilter("live")}
|
||||||
>
|
>
|
||||||
<Text>LIVE</Text>
|
<View style={{backgroundColor: "red", paddingHorizontal: 5, paddingVertical: 2, borderRadius: 5}}>
|
||||||
|
<Text style={{fontWeight: 'bold', color: 'white'}}>LIVE</Text>
|
||||||
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<View style={{
|
||||||
|
height: 60,
|
||||||
|
width: 2,
|
||||||
|
backgroundColor: "hsla(0, 0%, 37%, 1.00)",
|
||||||
|
marginHorizontal: 5,
|
||||||
|
borderRadius: 5,
|
||||||
|
|
||||||
|
}} />
|
||||||
|
|
||||||
{uniqueStreamingServices.map((serviceName) => {
|
{uniqueStreamingServices.map((serviceName) => {
|
||||||
const streamingService =
|
const streamingService =
|
||||||
streamingServices[
|
streamingServices[
|
||||||
@@ -134,9 +167,11 @@ export default function HomeScreen() {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={serviceName}
|
key={serviceName}
|
||||||
style={{
|
style={{
|
||||||
padding: 2,
|
padding: 5,
|
||||||
backgroundColor: "hsl(221, 39%, 80%)",
|
backgroundColor: "hsla(0, 0%, 29%, 1.00)",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
|
borderWidth: serviceName.includes(activeFilter) ? 2 : 0,
|
||||||
|
borderColor: "hsla(0, 100%, 50%, 1.00)",
|
||||||
}}
|
}}
|
||||||
onPress={() => handleFilter(serviceName)}
|
onPress={() => handleFilter(serviceName)}
|
||||||
>
|
>
|
||||||
@@ -154,12 +189,9 @@ export default function HomeScreen() {
|
|||||||
})}
|
})}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={{ flex: 1, paddingHorizontal: 10 }}>
|
||||||
{filteredShows.map((show) => {
|
{filteredShows.map((show) => {
|
||||||
const showLiveBadge = show.running;
|
const showLiveBadge = show.running;
|
||||||
const streamingService =
|
|
||||||
streamingServices[
|
|
||||||
`assets.images.streamingServices.${show.streamingService.toLowerCase()}`
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShowCard
|
<ShowCard
|
||||||
@@ -182,7 +214,7 @@ export default function HomeScreen() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
imageUri={show.bannerUri}
|
imageUri={show.bannerUri}
|
||||||
streamingServiceUri={streamingService}
|
streamingServicesUris={show.streamingService.split(', ').map(s => streamingServices[`assets.images.streamingServices.${s.toLowerCase()}`])}
|
||||||
genres={show.genres}
|
genres={show.genres}
|
||||||
{...(showLiveBadge
|
{...(showLiveBadge
|
||||||
? {
|
? {
|
||||||
@@ -193,6 +225,7 @@ export default function HomeScreen() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ export default function ParticipantScreen() {
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
contentContainerStyle={{ paddingBottom: 20, paddingTop: 10}}
|
contentContainerStyle={{ paddingBottom: 20, paddingTop: 10}}
|
||||||
|
|
||||||
>
|
>
|
||||||
<Text style={styles.participantName}>{name}</Text>
|
<Text style={styles.participantName}>{name}</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -214,7 +215,7 @@ export default function ParticipantScreen() {
|
|||||||
paddingLeft: 30,
|
paddingLeft: 30,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{appearances.map(({ show, seasons }) => {
|
{appearances.toReversed().map(({ show, seasons }) => {
|
||||||
const partners = Array.from(
|
const partners = Array.from(
|
||||||
new Map(
|
new Map(
|
||||||
seasons
|
seasons
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { getShowById, Show } from "@/apis/showApi";
|
||||||
import ParticipantDetails from "@/components/ParticipantDeatails";
|
import ParticipantDetails from "@/components/ParticipantDeatails";
|
||||||
import ShowInfo from "@/components/ui/ShowInfo";
|
import ShowInfo from "@/components/ui/ShowInfo";
|
||||||
import StackHeader from "@/components/ui/StackHeader";
|
import StackHeader from "@/components/ui/StackHeader";
|
||||||
import { useSeasonContext } from "@/contexts/SeasonContext";
|
import { useSeasonContext } from "@/contexts/SeasonContext";
|
||||||
|
import * as Haptics from 'expo-haptics';
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Dimensions,
|
Dimensions,
|
||||||
Image,
|
Image,
|
||||||
@@ -16,14 +18,18 @@ import styles from "./stackStyles/showDetailStyles";
|
|||||||
|
|
||||||
export default function ShowDetails() {
|
export default function ShowDetails() {
|
||||||
const {
|
const {
|
||||||
bannerUri,
|
// bannerUri,
|
||||||
description,
|
// description,
|
||||||
concept,
|
// concept,
|
||||||
genres,
|
// genres,
|
||||||
streamingService,
|
// streamingService,
|
||||||
id,
|
id,
|
||||||
endDate,
|
// endDate,
|
||||||
} = useLocalSearchParams();
|
} = useLocalSearchParams();
|
||||||
|
|
||||||
|
const [show, setShow] = useState<Show | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
const [selectedParticipants, setSelectedParticipants] =
|
const [selectedParticipants, setSelectedParticipants] =
|
||||||
React.useState<boolean>(true);
|
React.useState<boolean>(true);
|
||||||
const [selectedSeason, setSelectedSeason] = React.useState<number>(1);
|
const [selectedSeason, setSelectedSeason] = React.useState<number>(1);
|
||||||
@@ -48,6 +54,18 @@ export default function ShowDetails() {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!showId) return;
|
if (!showId) return;
|
||||||
|
|
||||||
|
const fetchShow = async () => {
|
||||||
|
try {
|
||||||
|
const data = await getShowById(showId);
|
||||||
|
setShow(data);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchShow();
|
||||||
|
|
||||||
let active = true;
|
let active = true;
|
||||||
(async () => {
|
(async () => {
|
||||||
const count = await fetchSeasonCount(showId);
|
const count = await fetchSeasonCount(showId);
|
||||||
@@ -128,15 +146,15 @@ export default function ShowDetails() {
|
|||||||
<ShowInfo
|
<ShowInfo
|
||||||
seasons={seasonCount}
|
seasons={seasonCount}
|
||||||
participants={participants.length}
|
participants={participants.length}
|
||||||
streamingService={streamingService as string}
|
streamingService={show?.streamingService as string}
|
||||||
startDate={startDate as string}
|
startDate={startDate as string}
|
||||||
endDate={endDate as string | null}
|
endDate={show?.endDate as string | null}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.showBannerLogoContainer}>
|
<View style={styles.showBannerLogoContainer}>
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
uri: bannerUri as string,
|
uri: show?.bannerUri as string,
|
||||||
}}
|
}}
|
||||||
style={styles.showBannerLogo}
|
style={styles.showBannerLogo}
|
||||||
resizeMode="cover"
|
resizeMode="cover"
|
||||||
@@ -194,6 +212,7 @@ export default function ShowDetails() {
|
|||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedSeason(season);
|
setSelectedSeason(season);
|
||||||
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={styles.seasonLabel}>{season}</Text>
|
<Text style={styles.seasonLabel}>{season}</Text>
|
||||||
@@ -241,10 +260,10 @@ export default function ShowDetails() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ParticipantDetails
|
<ParticipantDetails
|
||||||
description={description as string}
|
description={show?.description as string}
|
||||||
concept={concept as string}
|
concept={show?.concept as string}
|
||||||
genres={genres as string}
|
genres={show?.genres as string[]}
|
||||||
streamingService={streamingService as string}
|
streamingService={show?.streamingService as string}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const styles = StyleSheet.create({
|
|||||||
performedShowsSection: {
|
performedShowsSection: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
backgroundColor: "hsl(221, 39%, 0%)",
|
backgroundColor: 'hsl(221, 39%, 16%)',
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
},
|
},
|
||||||
performedShowsTitle: {
|
performedShowsTitle: {
|
||||||
@@ -175,7 +175,7 @@ const styles = StyleSheet.create({
|
|||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
backgroundColor: "hsl(221, 39%, 18%)",
|
backgroundColor: "hsl(221, 39%, 12%)",
|
||||||
marginLeft: 15,
|
marginLeft: 15,
|
||||||
marginTop: 15,
|
marginTop: 15,
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { View, Text, StyleSheet } from "react-native";
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
|
||||||
type ParticipantDetailsProps = {
|
type ParticipantDetailsProps = {
|
||||||
description: string;
|
description: string;
|
||||||
concept: string;
|
concept: string;
|
||||||
genres: string;
|
genres: string[];
|
||||||
streamingService: string;
|
streamingService: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ const ParticipantDetails = ({
|
|||||||
<Text style={styles.detailTitle}>Konzept:</Text>
|
<Text style={styles.detailTitle}>Konzept:</Text>
|
||||||
<Text style={styles.detailLabel}>{concept}</Text>
|
<Text style={styles.detailLabel}>{concept}</Text>
|
||||||
<Text style={styles.detailTitle}>Genres:</Text>
|
<Text style={styles.detailTitle}>Genres:</Text>
|
||||||
<Text style={styles.detailLabel}>{genres}</Text>
|
<Text style={styles.detailLabel}>{genres.join(', ')}</Text>
|
||||||
<Text style={styles.detailTitle}>Produktion:</Text>
|
<Text style={styles.detailTitle}>Produktion:</Text>
|
||||||
<Text style={styles.detailLabel}>{streamingService}</Text>
|
<Text style={styles.detailLabel}>{streamingService}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|||||||
|
|
||||||
type ShowCardProps = {
|
type ShowCardProps = {
|
||||||
imageUri: string;
|
imageUri: string;
|
||||||
streamingServiceUri: string;
|
streamingServicesUris: string[];
|
||||||
liveBadgeText?: string;
|
liveBadgeText?: string;
|
||||||
liveBadgeContainerStyle?: object;
|
liveBadgeContainerStyle?: object;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
@@ -13,7 +13,7 @@ type ShowCardProps = {
|
|||||||
|
|
||||||
const ShowCard = ({
|
const ShowCard = ({
|
||||||
imageUri,
|
imageUri,
|
||||||
streamingServiceUri,
|
streamingServicesUris,
|
||||||
liveBadgeText,
|
liveBadgeText,
|
||||||
liveBadgeContainerStyle,
|
liveBadgeContainerStyle,
|
||||||
genres,
|
genres,
|
||||||
@@ -32,14 +32,20 @@ const ShowCard = ({
|
|||||||
}}
|
}}
|
||||||
style={[StyleSheet.absoluteFillObject, { borderRadius: 35 }]}
|
style={[StyleSheet.absoluteFillObject, { borderRadius: 35 }]}
|
||||||
/>
|
/>
|
||||||
<View style={styles.streamingServiceIcon}>
|
|
||||||
|
<View style={{ flexDirection: 'row', width: '100%', justifyContent: 'flex-end', padding: 10, gap: 5}}>
|
||||||
|
{streamingServicesUris.length > 0 && streamingServicesUris.map((service) => (
|
||||||
<Image
|
<Image
|
||||||
|
key={service}
|
||||||
source={{
|
source={{
|
||||||
uri: streamingServiceUri,
|
uri: service,
|
||||||
}}
|
}}
|
||||||
style={[StyleSheet.absoluteFillObject, { borderRadius: 15 }]}
|
style={{ height: 45, width: 45, resizeMode: 'contain', borderRadius: 100}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{liveBadgeText && (
|
{liveBadgeText && (
|
||||||
<View style={liveBadgeContainerStyle}>
|
<View style={liveBadgeContainerStyle}>
|
||||||
<Text style={styles.liveBadgeText}>{liveBadgeText}</Text>
|
<Text style={styles.liveBadgeText}>{liveBadgeText}</Text>
|
||||||
@@ -70,8 +76,8 @@ const ShowCard = ({
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
showContainer: {
|
showContainer: {
|
||||||
width: "90%",
|
width: "100%",
|
||||||
height: 200,
|
height: 220,
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
borderRadius: 35,
|
borderRadius: 35,
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export default function StackHeader() {
|
|||||||
<Feather name="arrow-left" size={26} color="white" />
|
<Feather name="arrow-left" size={26} color="white" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Image style={styles.logo} source={{ uri: logoUriString }} />
|
<Image style={styles.logo} source={{ uri: logoUriString }} />
|
||||||
<TouchableOpacity>
|
{/* <TouchableOpacity>
|
||||||
<Feather name="share" size={26} color="white" />
|
<Feather name="share" size={26} color="white" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity> */}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
"name": "fltr-app",
|
"name": "fltr-app",
|
||||||
"main": "expo-router/entry",
|
"main": "expo-router/entry",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"displayName": "FLTR",
|
||||||
|
"description": "Reality TV",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
"reset-project": "node ./scripts/reset-project.js",
|
"reset-project": "node ./scripts/reset-project.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user