Skip to content

Commit

Permalink
Fix and refactor the code to work efficiently
Browse files Browse the repository at this point in the history
  • Loading branch information
kandiecindy committed Sep 23, 2024
1 parent 4af1fb4 commit e555803
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 213 deletions.
6 changes: 6 additions & 0 deletions app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export default function TabLayout() {
headerShown: false,
}}
/>
<Stack.Screen
name="speakers"
options={{
headerShown: false,
}}
/>
</Stack>
);
}
2 changes: 1 addition & 1 deletion app/(tabs)/home/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default function TabLayout() {
),
headerStyle: {
backgroundColor: Colors.palette.primary,
height: 120,
height: 110,
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
Expand Down
78 changes: 30 additions & 48 deletions app/(tabs)/home/speakers.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,50 @@
import React, { useEffect, useState } from 'react';
import { FlatList, View, ActivityIndicator, StyleSheet, Text } from 'react-native';
import palette from '@/constants/Colors'; // Use your color palette
import React from 'react';
import { FlatList, View, ActivityIndicator, StyleSheet } from 'react-native';
import palette from '@/constants/Colors';
import SpeakerCard from '@/components/SpeakerCard';
import { Speaker } from '@/components/types';
import BackgroundWrapper from '@/components/containers/BackgroundWrapper';
import { useFetchSpeakers } from '@/hooks/useFetchSpeakers';
import StyledText from '@/components/common/StyledText'; // Import StyledText
import { Stack } from 'expo-router';

const SpeakersTab = () => {
const [speakerList, setSpeakerList] = useState<Speaker[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);

const fetchSpeakers = async () => {
setLoading(true);
setError(false);
try {
const res = await fetch('https://sessionize.com/api/v2/d899srzm/view/Speakers');
if (!res.ok) throw new Error('Failed to fetch speakers');
const data = await res.json();
setSpeakerList(data);
} catch (error) {
setError(true);
} finally {
setLoading(false);
}
};

useEffect(() => {
fetchSpeakers();
}, []);
const { speakerList, loading, error } = useFetchSpeakers();

return (
<View style={styles.container}>
<Text style={styles.header}>Speakers</Text>

{loading ? (
<ActivityIndicator size="large" color={palette.palette.secondary} />
) : error ? (
<Text style={styles.error}>Failed to load speakers. Please try again later.</Text>
) : (
<FlatList
data={speakerList}
renderItem={({ item }) => <SpeakerCard speaker={item} />}
keyExtractor={(item) => item.id}
/>
)}
</View>
<BackgroundWrapper>
<View style={styles.container}>
<StyledText size="xl" font="semiBold" style={styles.header}>
Speakers
</StyledText>
{loading ? (
<ActivityIndicator size="large" color={palette.palette.secondary} />
) : error ? (
<StyledText variant="error" style={styles.error}>
Failed to load speakers. Please try again later.
</StyledText>
) : (
<FlatList
data={speakerList}
renderItem={({ item }) => <SpeakerCard speaker={item} />}
keyExtractor={(item) => item.id}
/>
)}
</View>
</BackgroundWrapper>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: palette.palette.primary,
},
header: {
fontSize: 24,
fontWeight: '700',
color: palette.palette.secondary,
textAlign: 'center',
marginBottom: 16,
marginBottom: 16, // Adjusted to ensure space between the header and the list
},
error: {
textAlign: 'center',
color: palette.palette.error,
marginTop: 16, // Added margin to position error message properly
},
});

Expand Down
214 changes: 99 additions & 115 deletions app/(tabs)/speakers/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,169 +1,153 @@
import React, { useEffect, useState } from 'react';
import { View, Text, Image, StyleSheet, ActivityIndicator, ScrollView, Linking } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import React, { useState, useMemo } from 'react';
import { View, ScrollView, ActivityIndicator, StyleSheet, TouchableOpacity, Linking, Image } from 'react-native';
import { Stack, useLocalSearchParams, useNavigation } from 'expo-router';
import palette from '@/constants/Colors';
import { FontAwesome } from '@expo/vector-icons'; // Importing FontAwesome icons for Twitter (X) and LinkedIn

type Speaker = {
id: string;
fullName: string;
profilePicture: string;
occupation: string;
bio: string;
socialMedia?: {
twitter?: string;
linkedin?: string;
};
sessions: {
time: string;
title: string;
}[];
};
import { useFetchSpeakers } from '@/hooks/useFetchSpeakers';
import SpeakerHeader from '@/components/headers/SpeakerHeader';
import BackgroundWrapper from '@/components/containers/BackgroundWrapper';
import StyledText from '@/components/common/StyledText';
import { FontAwesome } from '@expo/vector-icons';

const SpeakerPage = () => {
const { id } = useLocalSearchParams(); // Fetch dynamic ID from route
const [speaker, setSpeaker] = useState<Speaker | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Simulate fetching the speaker data based on the dynamic ID
const fetchSpeaker = async () => {
try {
const res = await fetch(`https://sessionize.com/api/v2/d899srzm/view/Speakers`);
const data = await res.json();
const speakerData = data.find((s: Speaker) => s.id === id); // Find speaker by dynamic ID
setSpeaker(speakerData);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
const navigation = useNavigation();
const { speakerList, loading, error } = useFetchSpeakers();
const [savedSessions, setSavedSessions] = useState<{ [key: string]: boolean }>({});

fetchSpeaker();
}, [id]);
const speaker = useMemo(() => speakerList.find((s) => s.id === id), [speakerList, id]);

if (loading) {
return <ActivityIndicator size="large" color={palette.palette.secondary} style={styles.loader} />;
}

if (!speaker) {
return <Text style={styles.error}>Speaker not found</Text>;
if (error || !speaker) {
return <StyledText variant="error" style={styles.error}>Speaker not found</StyledText>;
}

// Fallbacks for social media links
const twitterLink = speaker.socialMedia?.twitter || 'https://twitter.com'; // Fallback Twitter link
const linkedinLink = speaker.socialMedia?.linkedin || 'https://linkedin.com'; // Fallback LinkedIn link

return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.name}>{speaker.fullName}</Text>
<Image source={{ uri: speaker.profilePicture }} style={styles.image} resizeMode="cover" />
<Text style={styles.occupation}>{speaker.occupation}</Text>
</View>
const toggleSaveSession = (sessionIndex: number) => {
setSavedSessions((prevState) => ({
...prevState,
[sessionIndex]: !prevState[sessionIndex],
}));
};

<View style={styles.socialMediaContainer}>
<FontAwesome
name="twitter"
size={40}
color="#1DA1F2"
onPress={() => openURL(twitterLink)}
style={styles.socialIcon}
/>
<FontAwesome
name="linkedin"
size={40}
color="#0077B5"
onPress={() => openURL(linkedinLink)}
style={styles.socialIcon}
/>
</View>
const socialMediaLinks = [
{ name: 'twitter', url: speaker.socialMedia?.twitter || 'https://x.com/renderconke', icon: require('@/assets/images/x.png') },
{ name: 'linkedin', url: speaker.socialMedia?.linkedin || 'https://www.linkedin.com/company/renderconke/', icon: require('@/assets/images/linkedin.png')
},
];

<Text style={styles.bio}>{speaker.bio}</Text>
return (
<BackgroundWrapper>
<ScrollView contentContainerStyle={styles.container}>
{/* Back Button */}
<TouchableOpacity style={styles.backButton} onPress={() => navigation.goBack()}>
<FontAwesome name="arrow-left" size={18} color={palette.palette.secondary} />
</TouchableOpacity>

{speaker.sessions.map((session, index) => (
<View key={index} style={styles.sessionCard}>
<Text style={styles.sessionTime}>{session.time}</Text>
<Text style={styles.sessionTitle}>{session.title}</Text>
<SpeakerHeader name={speaker.fullName} occupation={speaker.occupation} profilePicture={speaker.profilePicture} />

<StyledText size="md" variant="text" style={styles.bio}>
{speaker.bio}
</StyledText>

<View style={styles.socialMediaContainer}>
{socialMediaLinks.map((social, index) => (
<TouchableOpacity key={index} onPress={() => Linking.openURL(social.url)} style={styles.socialIconImage}>
<Image source={social.icon} />
</TouchableOpacity>
))}
</View>
))}
</ScrollView>
);
};

const openURL = (url: string) => {
// Function to open social media links
Linking.openURL(url);
{speaker.sessions.map((session, index) => (
<View key={index} style={styles.sessionCard}>
<View style={styles.sessionHeader}>
<View>
<StyledText size="md" variant="text" style={styles.sessionTime}>
{session.time}
</StyledText>
<StyledText size="sm" font="bold" style={styles.sessionTitle}>
{session.name}
</StyledText>
</View>
<TouchableOpacity onPress={() => toggleSaveSession(index)}>
<FontAwesome
name={savedSessions[index] ? 'bookmark' : 'bookmark-o'}
size={20}
color={savedSessions[index] ? palette.palette.secondary : '#ccc'}
/>
</TouchableOpacity>
</View>
</View>
))}
</ScrollView>
</BackgroundWrapper>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: palette.palette.primary,
padding: 16,
justifyContent: 'center',
marginTop: 30,

},
backButton: {
position: 'absolute',
top: 10,
left: 10,
zIndex: 10,
padding: 8,
},
loader: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
error: {
color: palette.palette.error,
textAlign: 'center',
marginTop: 20,
},
header: {
alignItems: 'center',
bio: {
marginTop: 0,
marginBottom: 20,
},
name: {
fontSize: 24,
fontWeight: 'bold',
color: palette.palette.secondary,
},
image: {
width: 100,
height: 100,
borderRadius: 50,
marginVertical: 16,
},
occupation: {
fontSize: 18,
color: palette.palette.secondary,
marginBottom: 12,
lineHeight: 22,
},
socialMediaContainer: {
flexDirection: 'row',
justifyContent: 'center',
marginVertical: 12,
},
socialIcon: {
marginHorizontal: 15,
marginVertical: 15,
gap: 16,
},
bio: {
fontSize: 14,
color: palette.palette.text,
marginVertical: 20,
lineHeight: 22,
socialIconImage: {
borderWidth: 1,
borderColor: '#eee',
padding: 6,
borderRadius: 5,
backgroundColor: 'rgba(250, 250, 250, 0.2)',
},
sessionCard: {
backgroundColor: '#2c2c54',
backgroundColor: 'rgba(250, 250, 250, 0.2)',
padding: 16,
borderRadius: 12,
marginBottom: 16,
borderWidth: 1,
borderColor: '#eee',
},
sessionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 4,
},
sessionTime: {
fontSize: 16,
color: '#eee',
},
sessionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#fff',
marginTop: 4,
marginRight: 6,
flexShrink: 1,
flexWrap: 'wrap',
},
});

Expand Down
2 changes: 1 addition & 1 deletion app/(tabs)/speakers/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Stack } from 'expo-router';

export default function TabLayout() {
return (
<Stack>
<Stack screenOptions={{headerShown:false}}>
<Stack.Screen
name="[id]"
options={{
Expand Down
Binary file added assets/images/linkedin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit e555803

Please sign in to comment.