diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index 7fbd1ff..25fb9ad 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -10,6 +10,12 @@ export default function TabLayout() {
headerShown: false,
}}
/>
+
);
}
diff --git a/app/(tabs)/home/_layout.tsx b/app/(tabs)/home/_layout.tsx
index ffc3993..58ae6dd 100644
--- a/app/(tabs)/home/_layout.tsx
+++ b/app/(tabs)/home/_layout.tsx
@@ -81,7 +81,7 @@ export default function TabLayout() {
),
headerStyle: {
backgroundColor: Colors.palette.primary,
- height: 120,
+ height: 110,
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
diff --git a/app/(tabs)/home/speakers.tsx b/app/(tabs)/home/speakers.tsx
index f07ca9a..a5a914a 100644
--- a/app/(tabs)/home/speakers.tsx
+++ b/app/(tabs)/home/speakers.tsx
@@ -1,49 +1,36 @@
-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([]);
- 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 (
-
- Speakers
-
- {loading ? (
-
- ) : error ? (
- Failed to load speakers. Please try again later.
- ) : (
- }
- keyExtractor={(item) => item.id}
- />
- )}
-
+
+
+
+ Speakers
+
+ {loading ? (
+
+ ) : error ? (
+
+ Failed to load speakers. Please try again later.
+
+ ) : (
+ }
+ keyExtractor={(item) => item.id}
+ />
+ )}
+
+
);
};
@@ -51,18 +38,13 @@ 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
},
});
diff --git a/app/(tabs)/speakers/[id].tsx b/app/(tabs)/speakers/[id].tsx
index bd69ddc..566a8fc 100644
--- a/app/(tabs)/speakers/[id].tsx
+++ b/app/(tabs)/speakers/[id].tsx
@@ -1,107 +1,103 @@
-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(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 ;
}
- if (!speaker) {
- return Speaker not found;
+ if (error || !speaker) {
+ return Speaker not found;
}
- // 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 (
-
-
- {speaker.fullName}
-
- {speaker.occupation}
-
+ const toggleSaveSession = (sessionIndex: number) => {
+ setSavedSessions((prevState) => ({
+ ...prevState,
+ [sessionIndex]: !prevState[sessionIndex],
+ }));
+ };
-
- openURL(twitterLink)}
- style={styles.socialIcon}
- />
- openURL(linkedinLink)}
- style={styles.socialIcon}
- />
-
+ 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')
+ },
+ ];
- {speaker.bio}
+ return (
+
+
+ {/* Back Button */}
+ navigation.goBack()}>
+
+
- {speaker.sessions.map((session, index) => (
-
- {session.time}
- {session.title}
+
+
+
+ {speaker.bio}
+
+
+
+ {socialMediaLinks.map((social, index) => (
+ Linking.openURL(social.url)} style={styles.socialIconImage}>
+
+
+ ))}
- ))}
-
- );
-};
-const openURL = (url: string) => {
- // Function to open social media links
- Linking.openURL(url);
+ {speaker.sessions.map((session, index) => (
+
+
+
+
+ {session.time}
+
+
+ {session.name}
+
+
+ toggleSaveSession(index)}>
+
+
+
+
+ ))}
+
+
+ );
};
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,
@@ -109,61 +105,49 @@ const styles = StyleSheet.create({
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',
},
});
diff --git a/app/(tabs)/speakers/_layout.tsx b/app/(tabs)/speakers/_layout.tsx
index 341f5b9..ab62cb7 100644
--- a/app/(tabs)/speakers/_layout.tsx
+++ b/app/(tabs)/speakers/_layout.tsx
@@ -3,7 +3,7 @@ import { Stack } from 'expo-router';
export default function TabLayout() {
return (
-
+
= ({ speaker }) => {
) : (
- No Image
+ No Image
)}
- {speaker.fullName}
+
+ {speaker.fullName}
+
- {speaker.sessions[0]?.name || 'Session you cant afford to miss!'}
+
+ {speaker.sessions[0]?.name || 'Session you can’t afford to miss!'}
+
@@ -29,8 +34,8 @@ const SpeakerCard: React.FC = ({ speaker }) => {
const styles = StyleSheet.create({
card: {
- backgroundColor: '#351e4a', // primary color
- borderRadius: 12,
+ backgroundColor: 'rgba(250, 250, 250,0.15)',
+ borderRadius: 5,
flexDirection: 'row',
alignItems: 'center',
padding: 8,
@@ -39,6 +44,8 @@ const styles = StyleSheet.create({
shadowOpacity: 0.25,
shadowRadius: 6,
shadowOffset: { width: 0, height: 2 },
+ borderWidth: 1,
+ borderColor: '#eee',
},
image: {
width: 80,
@@ -58,19 +65,15 @@ const styles = StyleSheet.create({
},
imageFallbackText: {
color: '#fff',
- fontSize: 12,
},
textContainer: {
flex: 1,
},
name: {
- fontSize: 18,
- fontWeight: '700',
- color: '#eee712', // secondary color
+ marginBottom: 4, // Adjusted for spacing between name and occupation
},
occupation: {
- fontSize: 14,
- color: '#fff', // text color
+ color: '#fff', // You can customize the color here if needed
marginTop: 4,
},
});
diff --git a/components/Speakers.tsx b/components/Speakers.tsx
index 63d5708..27b6da8 100644
--- a/components/Speakers.tsx
+++ b/components/Speakers.tsx
@@ -1,43 +1,25 @@
-import React, { useEffect, useState } from 'react';
-import { FlatList, View, ActivityIndicator, StyleSheet, Text } from 'react-native';
-import SpeakerCard from './SpeakerCard'; // Assuming it's in the same directory
+import React from 'react';
+import { FlatList, View, ActivityIndicator, StyleSheet } from 'react-native';
+import SpeakerCard from './SpeakerCard';
import palette from '@/constants/Colors'; // Use default import if Colors is exported as default
-import { Speaker } from './types'; // Import the Speaker type
+import { useFetchSpeakers } from '@/hooks/useFetchSpeakers'; // Use the custom hook
import StyledText from './common/StyledText';
const Speakers = () => {
- const [speakerList, setSpeakerList] = useState([]); // Use Speaker[] type for the array
- 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(); // Use the custom hook to fetch speakers
return (
+ {/* StyledText replaces Text for a consistent font style */}
Speakers
{loading ? (
- // Access secondary color correctly
+
) : error ? (
- Failed to load speakers. Please try again later.
+
+ Failed to load speakers. Please try again later.
+
) : (
;
+}
+
+const BackgroundWrapper: React.FC = ({ children, style }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ background: {
+ flex: 1,
+ width: '100%',
+ height: '100%',
+ backgroundColor: palette.palette.primary,
+ },
+});
+
+export default BackgroundWrapper;
diff --git a/components/headers/SpeakerHeader.tsx b/components/headers/SpeakerHeader.tsx
new file mode 100644
index 0000000..f8b6e7f
--- /dev/null
+++ b/components/headers/SpeakerHeader.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import { View, Image, StyleSheet } from 'react-native';
+import palette from '@/constants/Colors';
+import StyledText from '@/components/common/StyledText'; // Import the StyledText component
+
+interface SpeakerHeaderProps {
+ name: string;
+ occupation: string;
+ profilePicture: string;
+}
+
+const SpeakerHeader: React.FC = ({ name, occupation, profilePicture }) => {
+ return (
+
+ {/* Replaced Text with StyledText */}
+
+ {name}
+
+
+
+ {occupation}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ header: {
+ alignItems: 'center',
+ marginBottom: 20,
+ },
+ name: {
+ marginBottom: 8,
+ color: palette.palette.secondary
+ },
+ image: {
+ width: 150,
+ height: 150,
+ borderRadius: 20,
+ marginVertical: 16,
+ },
+ occupation: {
+ marginBottom: 12,
+ },
+});
+
+export default SpeakerHeader;
diff --git a/components/types.ts b/components/types.ts
index 757176f..e5bca63 100644
--- a/components/types.ts
+++ b/components/types.ts
@@ -1,7 +1,15 @@
-// types.ts
export type Speaker = {
id: string;
fullName: string;
profilePicture: string;
- sessions: { name: string }[];
+ occupation: string; // Add occupation property
+ bio: string; // Add bio property (since it is used in the speaker page)
+ socialMedia?: {
+ twitter?: string;
+ linkedin?: string;
+ }; // Add optional socialMedia object with twitter and linkedin fields
+ sessions: {
+ name: string; // Add time property to each session
+ time: string; // Add title property to each session
+ }[];
};
diff --git a/hooks/useFetchSpeakers.tsx b/hooks/useFetchSpeakers.tsx
new file mode 100644
index 0000000..2cb8b84
--- /dev/null
+++ b/hooks/useFetchSpeakers.tsx
@@ -0,0 +1,29 @@
+import { useState, useEffect } from 'react';
+import { Speaker } from '@/components/types';
+
+export const useFetchSpeakers = () => {
+ const [speakerList, setSpeakerList] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(false);
+
+ useEffect(() => {
+ 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);
+ }
+ };
+
+ fetchSpeakers();
+ }, []);
+
+ return { speakerList, loading, error };
+};