WIP: update trip cards and Spotify handling
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ImageBackground, Alert, ActivityIndicator } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { NavigationProp, useFocusEffect } from '@react-navigation/native';
|
||||
@@ -6,11 +6,119 @@ import { Navigation, Clock, MapPin } from 'lucide-react-native';
|
||||
import { colors } from '../../utils/colors';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { supabase } from '../../services/supabase';
|
||||
import { getDestinationLandmarkImage } from '../../services/destinationImage';
|
||||
|
||||
interface Props {
|
||||
navigation: NavigationProp<any>;
|
||||
}
|
||||
|
||||
type TripCardProps = {
|
||||
trip: any;
|
||||
};
|
||||
|
||||
const maskGoogleKeyInImageUrl = (url: string | null) => url?.replace(/([?&]key=)[^&]+/, '$1[REDACTED]') || null;
|
||||
|
||||
function TripCard({ trip }: TripCardProps) {
|
||||
const [imageUrl, setImageUrl] = useState<string | null>(trip.destination_image_url || null);
|
||||
const [landmarkName, setLandmarkName] = useState<string | null>(trip.destination_landmark_name || null);
|
||||
const [placeId, setPlaceId] = useState<string | null>(trip.destination_place_id || trip.place_id || null);
|
||||
const tripTitle = trip.title || trip.destination || 'Viagem';
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
console.log('[TripCardImageDebug] initial', {
|
||||
tripName: tripTitle,
|
||||
destination: trip.destination,
|
||||
placeId: trip.destination_place_id || trip.place_id || null,
|
||||
imageUrl: trip.destination_image_url || null,
|
||||
});
|
||||
|
||||
if (trip.destination_image_url || !trip.destination) return;
|
||||
|
||||
getDestinationLandmarkImage(trip.destination, { tripTitle }).then(result => {
|
||||
if (!isMounted) return;
|
||||
setImageUrl(result.imageUrl);
|
||||
setLandmarkName(result.landmarkName);
|
||||
setPlaceId(result.placeId);
|
||||
console.log('[TripCardImageDebug] fetched', {
|
||||
tripName: tripTitle,
|
||||
destination: trip.destination,
|
||||
placeId: result.placeId,
|
||||
imageUrl: maskGoogleKeyInImageUrl(result.imageUrl),
|
||||
source: result.source,
|
||||
fallbackReason: result.fallbackReason,
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [trip.destination, trip.destination_image_url, trip.destination_place_id, trip.place_id, tripTitle]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('[TripCardImageDebug] render_image_uri', {
|
||||
tripName: tripTitle,
|
||||
destination: trip.destination,
|
||||
placeId,
|
||||
imageUrl: maskGoogleKeyInImageUrl(imageUrl),
|
||||
hasValidRemoteUri: Boolean(imageUrl?.startsWith('https://')),
|
||||
fallbackReason: imageUrl ? null : 'missing_image_url',
|
||||
});
|
||||
}, [trip.destination, tripTitle, placeId, imageUrl]);
|
||||
|
||||
const imageContent = (
|
||||
<View style={styles.imageOverlay}>
|
||||
<Text style={styles.tripImageTitle} numberOfLines={2}>{tripTitle}</Text>
|
||||
<Text style={styles.tripImageSubtitle} numberOfLines={1}>
|
||||
{landmarkName || trip.destination}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.mainTripCard}>
|
||||
{imageUrl ? (
|
||||
<ImageBackground source={{ uri: imageUrl }} style={styles.tripImage} imageStyle={styles.tripImageStyle}>
|
||||
{imageContent}
|
||||
</ImageBackground>
|
||||
) : (
|
||||
<View style={[styles.tripImage, styles.tripImageFallback]}>
|
||||
<View style={styles.tripImageFallbackGlow} />
|
||||
<View style={styles.tripImageFallbackAccent} />
|
||||
{imageContent}
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={styles.tripContent}>
|
||||
<View style={styles.statsRow}>
|
||||
<View style={styles.statItem}>
|
||||
<Navigation color={colors.primary} size={16} />
|
||||
<Text style={styles.statText}>{trip.distance}</Text>
|
||||
</View>
|
||||
<View style={styles.statDot} />
|
||||
<View style={styles.statItem}>
|
||||
<Clock color={colors.primary} size={16} />
|
||||
<Text style={styles.statText}>{trip.duration}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.itinerarySnippet}>
|
||||
<View style={styles.timeline}>
|
||||
<View style={styles.dot} />
|
||||
<View style={styles.line} />
|
||||
<View style={[styles.dot, styles.dotEmpty]} />
|
||||
</View>
|
||||
<View style={styles.locations}>
|
||||
<Text style={styles.locationText}>{trip.origin}</Text>
|
||||
<Text style={styles.locationText}>{trip.destination}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomeScreen({ navigation }: Props) {
|
||||
const { user } = useAuth();
|
||||
const userName = user?.user_metadata?.display_name || user?.user_metadata?.name || user?.email || user?.user_metadata?.email || 'Viajante';
|
||||
@@ -68,38 +176,7 @@ export default function HomeScreen({ navigation }: Props) {
|
||||
{loading ? (
|
||||
<ActivityIndicator size="large" color={colors.primary} style={{ marginTop: 40 }} />
|
||||
) : trips.length > 0 ? (
|
||||
trips.map(trip => (
|
||||
<View key={trip.id} style={styles.mainTripCard}>
|
||||
<View style={[styles.tripImage, { backgroundColor: colors.inputBackground, borderTopLeftRadius: 24, borderTopRightRadius: 24 }]} />
|
||||
<View style={styles.tripContent}>
|
||||
<Text style={styles.tripTitle}>{trip.title}</Text>
|
||||
|
||||
<View style={styles.statsRow}>
|
||||
<View style={styles.statItem}>
|
||||
<Navigation color={colors.primary} size={16} />
|
||||
<Text style={styles.statText}>{trip.distance}</Text>
|
||||
</View>
|
||||
<View style={styles.statDot} />
|
||||
<View style={styles.statItem}>
|
||||
<Clock color={colors.primary} size={16} />
|
||||
<Text style={styles.statText}>{trip.duration}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.itinerarySnippet}>
|
||||
<View style={styles.timeline}>
|
||||
<View style={styles.dot} />
|
||||
<View style={styles.line} />
|
||||
<View style={[styles.dot, styles.dotEmpty]} />
|
||||
</View>
|
||||
<View style={styles.locations}>
|
||||
<Text style={styles.locationText}>{trip.origin}</Text>
|
||||
<Text style={styles.locationText}>{trip.destination}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
trips.map(trip => <TripCard key={trip.id} trip={trip} />)
|
||||
) : (
|
||||
<View style={styles.promptCard}>
|
||||
<Text style={styles.promptTitle}>Pronto para a próxima?</Text>
|
||||
@@ -167,23 +244,54 @@ const styles = StyleSheet.create({
|
||||
tripImage: {
|
||||
height: 180,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
imageOverlay: {
|
||||
padding: 20,
|
||||
backgroundColor: 'rgba(0,0,0,0.3)',
|
||||
overflow: 'hidden',
|
||||
borderTopLeftRadius: 24,
|
||||
borderTopRightRadius: 24,
|
||||
},
|
||||
tripTitle: {
|
||||
color: colors.white,
|
||||
fontSize: 22,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 4,
|
||||
tripImageStyle: {
|
||||
borderTopLeftRadius: 24,
|
||||
borderTopRightRadius: 24,
|
||||
},
|
||||
tripDate: {
|
||||
tripImageFallback: {
|
||||
backgroundColor: '#2B2018',
|
||||
position: 'relative',
|
||||
},
|
||||
tripImageFallbackGlow: {
|
||||
position: 'absolute',
|
||||
top: -50,
|
||||
right: -40,
|
||||
width: 180,
|
||||
height: 180,
|
||||
borderRadius: 90,
|
||||
backgroundColor: 'rgba(255, 122, 0, 0.35)',
|
||||
},
|
||||
tripImageFallbackAccent: {
|
||||
position: 'absolute',
|
||||
left: -30,
|
||||
bottom: -60,
|
||||
width: 190,
|
||||
height: 190,
|
||||
borderRadius: 95,
|
||||
backgroundColor: 'rgba(255, 214, 180, 0.16)',
|
||||
},
|
||||
imageOverlay: {
|
||||
padding: 20,
|
||||
paddingTop: 80,
|
||||
backgroundColor: 'rgba(0,0,0,0.42)',
|
||||
borderTopLeftRadius: 24,
|
||||
borderTopRightRadius: 24,
|
||||
},
|
||||
tripImageTitle: {
|
||||
color: colors.white,
|
||||
fontSize: 14,
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
marginBottom: 6,
|
||||
},
|
||||
tripImageSubtitle: {
|
||||
color: colors.white,
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
opacity: 0.92,
|
||||
},
|
||||
tripContent: {
|
||||
padding: 20,
|
||||
|
||||
Reference in New Issue
Block a user