modified: files to ios26 ui/ux
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import ParticipantDetails from "@/components/ui/ParticipantDeatails";
|
||||
import ShowInfo from "@/components/ui/ShowInfo";
|
||||
import StackHeader from "@/components/ui/StackHeader";
|
||||
import {
|
||||
useSeasonCount,
|
||||
useSeasonDates,
|
||||
@@ -11,9 +10,11 @@ import * as Haptics from "expo-haptics";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import React from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Dimensions,
|
||||
Image,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
@@ -21,15 +22,17 @@ import {
|
||||
import styles from "./stackStyles/showDetailStyles";
|
||||
|
||||
export default function ShowDetails() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { id, logoUri } = useLocalSearchParams();
|
||||
const showId = Number(id);
|
||||
const logoUriString = Array.isArray(logoUri) ? logoUri[0] : logoUri;
|
||||
|
||||
const [selectedParticipants, setSelectedParticipants] =
|
||||
React.useState<boolean>(true);
|
||||
const [selectedSeason, setSelectedSeason] = React.useState<number>(1);
|
||||
|
||||
const { data: show } = useShow(showId);
|
||||
const { data: seasonCount = 0 } = useSeasonCount(showId);
|
||||
const { data: show, isLoading: showLoading } = useShow(showId);
|
||||
const { data: seasonCount = 0, isLoading: seasonCountLoading } =
|
||||
useSeasonCount(showId);
|
||||
const {
|
||||
data: participants,
|
||||
isLoading: pLoading,
|
||||
@@ -40,7 +43,7 @@ export default function ShowDetails() {
|
||||
|
||||
const sortedParticipants = React.useMemo(() => {
|
||||
return [...participants].sort((a, b) =>
|
||||
a.name.localeCompare(b.name, "de", { sensitivity: "base" })
|
||||
a.name.localeCompare(b.name, "de", { sensitivity: "base" }),
|
||||
);
|
||||
}, [participants]);
|
||||
|
||||
@@ -62,156 +65,227 @@ export default function ShowDetails() {
|
||||
}, [startDate]);
|
||||
|
||||
const handleOpenParticipant = React.useCallback(
|
||||
(p: { id: number; name: string }) => {
|
||||
(p: { id: number; name: string; imageUri?: string }) => {
|
||||
router.push({
|
||||
pathname: "/participant",
|
||||
params: {
|
||||
participantId: p.id,
|
||||
name: p.name,
|
||||
imageUri: p.imageUri || "",
|
||||
originShowId: String(showId),
|
||||
originSeason: String(selectedSeason),
|
||||
},
|
||||
});
|
||||
},
|
||||
[showId, selectedSeason]
|
||||
[showId, selectedSeason],
|
||||
);
|
||||
|
||||
const isInitialLoading = showLoading || seasonCountLoading;
|
||||
|
||||
return (
|
||||
<View style={styles.mainContainer}>
|
||||
<StackHeader />
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
paddingBottom: Dimensions.get("window").height * 0.1,
|
||||
}}
|
||||
>
|
||||
{formattedStartDate ? (
|
||||
<Text style={styles.startDate}>{formattedStartDate}</Text>
|
||||
) : null}
|
||||
<ShowInfo
|
||||
seasons={seasonCount}
|
||||
participants={participants.length}
|
||||
streamingService={show?.streamingService as string}
|
||||
startDate={startDate as string}
|
||||
endDate={show?.endDate as string | null}
|
||||
/>
|
||||
|
||||
<View style={styles.showBannerLogoContainer}>
|
||||
<Image
|
||||
source={{
|
||||
uri: show?.bannerUri as string,
|
||||
}}
|
||||
style={styles.showBannerLogo}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
{isInitialLoading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color="#199edb" />
|
||||
</View>
|
||||
<View style={styles.infoContainner}>
|
||||
<TouchableOpacity onPress={() => setSelectedParticipants(true)}>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoLabel,
|
||||
{
|
||||
fontWeight: selectedParticipants ? "bold" : "normal",
|
||||
color: selectedParticipants ? "#199edb" : "hsl(0, 0%, 65%)",
|
||||
},
|
||||
]}
|
||||
>
|
||||
Teilnehmer
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => setSelectedParticipants(false)}>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoLabel,
|
||||
{
|
||||
fontWeight: !selectedParticipants ? "bold" : "normal",
|
||||
color: !selectedParticipants ? "#199edb" : "hsl(0, 0%, 65%)",
|
||||
},
|
||||
]}
|
||||
>
|
||||
Details
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{selectedParticipants ? (
|
||||
<>
|
||||
<View style={styles.seasonsSection}>
|
||||
<Text style={styles.seasonsLabel}>Staffeln</Text>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.seasonList}
|
||||
>
|
||||
{Array.from({ length: seasonCount }, (_, idx) => idx + 1).map(
|
||||
(season) => (
|
||||
<TouchableOpacity
|
||||
key={season}
|
||||
style={[
|
||||
styles.seasonContainer,
|
||||
{
|
||||
backgroundColor:
|
||||
selectedSeason === season
|
||||
? "#199edb"
|
||||
: "hsl(0, 0%, 20%)",
|
||||
},
|
||||
]}
|
||||
onPress={() => {
|
||||
setSelectedSeason(season);
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
}}
|
||||
>
|
||||
<Text style={styles.seasonLabel}>{season}</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
) : (
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
paddingBottom: Dimensions.get("window").height * 0.1,
|
||||
}}
|
||||
>
|
||||
{logoUriString ? (
|
||||
<View style={styles.logoContainer}>
|
||||
<Image
|
||||
source={{ uri: logoUriString }}
|
||||
style={styles.showLogo}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
styles.participantsDetailsContainer,
|
||||
styles.participantSection,
|
||||
]}
|
||||
>
|
||||
{pError && (
|
||||
<Text style={{ color: "tomato", marginBottom: 8 }}>
|
||||
{pError}
|
||||
</Text>
|
||||
)}
|
||||
{!pLoading && !pError && participants.length === 0 && (
|
||||
<Text style={{ color: "gray" }}>Keine Teilnehmer.</Text>
|
||||
)}
|
||||
{sortedParticipants.map((p) => (
|
||||
<TouchableOpacity
|
||||
key={p.id}
|
||||
style={[
|
||||
styles.participantContainer,
|
||||
{ backgroundColor: "hsl(336, 79%, 63%)" },
|
||||
]}
|
||||
onPress={() => handleOpenParticipant(p)}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: p.imageUri }}
|
||||
style={{ width: "100%", height: "100%", borderRadius: 10 }}
|
||||
resizeMode="cover"
|
||||
blurRadius={p.imageUri.includes("pravatar") ? 16 : 0}
|
||||
/>
|
||||
<Text style={styles.participantLabel} numberOfLines={2}>
|
||||
{p.name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<ParticipantDetails
|
||||
description={show?.description as string}
|
||||
concept={show?.concept as string}
|
||||
genres={show?.genres as string[]}
|
||||
) : null}
|
||||
{formattedStartDate ? (
|
||||
<Text style={styles.startDate}>{formattedStartDate}</Text>
|
||||
) : null}
|
||||
<ShowInfo
|
||||
seasons={seasonCount}
|
||||
participants={participants.length}
|
||||
streamingService={show?.streamingService as string}
|
||||
startDate={startDate as string}
|
||||
endDate={show?.endDate as string | null}
|
||||
/>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
<View style={styles.showBannerLogoContainer}>
|
||||
<Image
|
||||
source={{
|
||||
uri: show?.bannerUri as string,
|
||||
}}
|
||||
style={styles.showBannerLogo}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.infoContainner}>
|
||||
<TouchableOpacity
|
||||
onPress={() => setSelectedParticipants(true)}
|
||||
style={{
|
||||
backgroundColor: selectedParticipants
|
||||
? "rgba(25,158,219,0.2)"
|
||||
: "transparent",
|
||||
borderRadius: 20,
|
||||
borderWidth: selectedParticipants
|
||||
? StyleSheet.hairlineWidth
|
||||
: 0,
|
||||
borderColor: "rgba(25,158,219,0.4)",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoLabel,
|
||||
{
|
||||
fontWeight: selectedParticipants ? "700" : "500",
|
||||
color: selectedParticipants
|
||||
? "#199edb"
|
||||
: "rgba(255,255,255,0.45)",
|
||||
},
|
||||
]}
|
||||
>
|
||||
Teilnehmer
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => setSelectedParticipants(false)}
|
||||
style={{
|
||||
backgroundColor: !selectedParticipants
|
||||
? "rgba(25,158,219,0.2)"
|
||||
: "transparent",
|
||||
borderRadius: 20,
|
||||
borderWidth: !selectedParticipants
|
||||
? StyleSheet.hairlineWidth
|
||||
: 0,
|
||||
borderColor: "rgba(25,158,219,0.4)",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoLabel,
|
||||
{
|
||||
fontWeight: !selectedParticipants ? "700" : "500",
|
||||
color: !selectedParticipants
|
||||
? "#199edb"
|
||||
: "rgba(255,255,255,0.45)",
|
||||
},
|
||||
]}
|
||||
>
|
||||
Details
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{selectedParticipants ? (
|
||||
<>
|
||||
<View style={styles.seasonsSection}>
|
||||
<Text style={styles.seasonsLabel}>Staffeln</Text>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.seasonList}
|
||||
>
|
||||
{Array.from({ length: seasonCount }, (_, idx) => idx + 1).map(
|
||||
(season) => (
|
||||
<TouchableOpacity
|
||||
key={season}
|
||||
style={[
|
||||
styles.seasonContainer,
|
||||
{
|
||||
backgroundColor:
|
||||
selectedSeason === season
|
||||
? "#199edb"
|
||||
: "rgba(255,255,255,0.08)",
|
||||
borderColor:
|
||||
selectedSeason === season
|
||||
? "rgba(25,158,219,0.3)"
|
||||
: "rgba(255,255,255,0.06)",
|
||||
},
|
||||
]}
|
||||
onPress={() => {
|
||||
setSelectedSeason(season);
|
||||
Haptics.impactAsync(
|
||||
Haptics.ImpactFeedbackStyle.Light,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Text style={styles.seasonLabel}>{season}</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
styles.participantsDetailsContainer,
|
||||
styles.participantSection,
|
||||
]}
|
||||
>
|
||||
{pError && (
|
||||
<Text
|
||||
style={{ color: "tomato", marginBottom: 8, fontSize: 13 }}
|
||||
>
|
||||
{pError}
|
||||
</Text>
|
||||
)}
|
||||
{pLoading && (
|
||||
<View style={styles.sectionLoading}>
|
||||
<ActivityIndicator size="small" color="#199edb" />
|
||||
</View>
|
||||
)}
|
||||
{!pLoading && !pError && participants.length === 0 && (
|
||||
<Text
|
||||
style={{ color: "rgba(255,255,255,0.4)", fontSize: 14 }}
|
||||
>
|
||||
Keine Teilnehmer.
|
||||
</Text>
|
||||
)}
|
||||
{!pLoading &&
|
||||
sortedParticipants.map((p) => (
|
||||
<TouchableOpacity
|
||||
key={p.id}
|
||||
style={styles.participantWrapper}
|
||||
onPress={() => handleOpenParticipant(p)}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.participantContainer,
|
||||
{ backgroundColor: "hsl(336, 79%, 63%)" },
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: p.imageUri }}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
borderRadius: 16,
|
||||
}}
|
||||
resizeMode="cover"
|
||||
blurRadius={p.imageUri.includes("pravatar") ? 16 : 0}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.participantLabel} numberOfLines={2}>
|
||||
{p.name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<ParticipantDetails
|
||||
description={show?.description as string}
|
||||
concept={show?.concept as string}
|
||||
genres={show?.genres as string[]}
|
||||
streamingService={show?.streamingService as string}
|
||||
/>
|
||||
)}
|
||||
</ScrollView>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user