diff --git a/frontend/sac-mobile/app/(app)/(tabs)/_layout.tsx b/frontend/sac-mobile/app/(app)/(tabs)/_layout.tsx index 65e279e4a..020ad6f29 100644 --- a/frontend/sac-mobile/app/(app)/(tabs)/_layout.tsx +++ b/frontend/sac-mobile/app/(app)/(tabs)/_layout.tsx @@ -10,6 +10,14 @@ const HomeTabBarIcon = ({ color }: { color: string }) => ( ); +const ProfileTabBarIcon = ({ color }: { color: string }) => ( + +); + +const SearchTabBarIcon = ({ color }: { color: string }) => ( + +); + const Layout = () => { const { isLoggedIn, fetchUser } = useAuthStore(); @@ -28,6 +36,16 @@ const Layout = () => { }} redirect={!isLoggedIn} /> + + ); }; diff --git a/frontend/sac-mobile/app/(app)/(tabs)/index.tsx b/frontend/sac-mobile/app/(app)/(tabs)/index.tsx index 2b33abab9..57284d3c6 100644 --- a/frontend/sac-mobile/app/(app)/(tabs)/index.tsx +++ b/frontend/sac-mobile/app/(app)/(tabs)/index.tsx @@ -1,20 +1,19 @@ import React from 'react'; -import { Text, View } from 'react-native'; +import { Text, View, FlatList } from 'react-native'; import { Link } from 'expo-router'; -import { Button } from '@/components/button'; import { useAuthStore } from '@/hooks/use-auth'; import { useEvents } from '@/hooks/use-event'; +import { Event } from '@/types/event'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import { Club } from '@/types/club'; +import { clubs } from '@/data/clubs'; const Home = () => { - const { signOut, user } = useAuthStore(); + const { user } = useAuthStore(); const { data: events, isLoading, error } = useEvents(); - const handleSignOut = async () => { - signOut(); - }; - if (isLoading) { return Loading...; } @@ -23,25 +22,48 @@ const Home = () => { return Error: {error.message}; } + const renderEvent = ({ item: event }: { item: Event }) => ( + + {event.name} + + ); + + const renderClub = ({ item: club }: { item: Club }) => ( + + {club.name} + + ); + return ( - + Welcome {user?.first_name} - - {/* - Event - */} - {events?.map((event) => ( - - {event.name} - - ))} - + item.id.toString()} + /> + item.id.toString()} + /> + ); }; diff --git a/frontend/sac-mobile/app/(app)/(tabs)/profile.tsx b/frontend/sac-mobile/app/(app)/(tabs)/profile.tsx new file mode 100644 index 000000000..04c8ad14f --- /dev/null +++ b/frontend/sac-mobile/app/(app)/(tabs)/profile.tsx @@ -0,0 +1,20 @@ +import { View, Text } from 'react-native'; +import React from 'react'; +import { Button } from '@/components/button'; +import { useAuthStore } from '@/hooks/use-auth'; + +const Profile = () => { + const { signOut } = useAuthStore(); + + const handleSignOut = async () => { + signOut(); + }; + + return ( + + + + ); +} + +export default Profile; \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/(tabs)/search.tsx b/frontend/sac-mobile/app/(app)/(tabs)/search.tsx new file mode 100644 index 000000000..67154711b --- /dev/null +++ b/frontend/sac-mobile/app/(app)/(tabs)/search.tsx @@ -0,0 +1,12 @@ +import { View, Text } from 'react-native' +import React from 'react' + +const Search = () => { + return ( + + Search + + ) +} + +export default Search \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/_layout.tsx b/frontend/sac-mobile/app/(app)/_layout.tsx index 0eac97114..76e0266a6 100644 --- a/frontend/sac-mobile/app/(app)/_layout.tsx +++ b/frontend/sac-mobile/app/(app)/_layout.tsx @@ -1,9 +1,113 @@ import React from 'react'; import { Stack } from 'expo-router'; +import { MenuView } from '@react-native-menu/menu'; +import { LeftArrow } from '@/components/left-arrow'; +import { MaterialCommunityIcons } from '@expo/vector-icons'; +import { View, Platform, StatusBar } from 'react-native'; const Layout = () => { - return ; + return ( + + + ( + + ), + headerLeft: () => , + headerRight: () => { + return ( + { + console.warn(JSON.stringify(nativeEvent)); + }} + actions={[ + { + id: 'share', + title: 'Share Event', + image: Platform.select({ + ios: 'square.and.arrow.up', + android: 'share-variant' + }) + }, + { + id: 'report', + title: 'Report Event', + image: Platform.select({ + ios: 'person.crop.circle.badge.exclamationmark.fill', + android: 'person-circle-outline' + }) + } + ]} + > + + + ); + } + }} + /> + ( + + ), + headerLeft: () => , + headerRight: () => { + return ( + { + console.warn(JSON.stringify(nativeEvent)); + }} + actions={[ + { + id: 'share', + title: 'Share Club', + image: Platform.select({ + ios: 'square.and.arrow.up', + android: 'share-variant' + }) + }, + { + id: 'report', + title: 'Report Club', + image: Platform.select({ + ios: 'person.crop.circle.badge.exclamationmark.fill', + android: 'person-circle-outline' + }) + } + ]} + > + + + ); + } + }} + /> + + ); }; export default Layout; diff --git a/frontend/sac-mobile/app/(app)/club/[id].tsx b/frontend/sac-mobile/app/(app)/club/[id].tsx new file mode 100644 index 000000000..e12436aff --- /dev/null +++ b/frontend/sac-mobile/app/(app)/club/[id].tsx @@ -0,0 +1,21 @@ +import { View, Text, SafeAreaView } from 'react-native' +import React from 'react' +import { Link, Stack, useLocalSearchParams } from 'expo-router'; + +const ClubPage = () => { + const { id } = useLocalSearchParams<{ id: string }>(); + + return ( + + + ClubPage + + FAQ + + + ) +} + +export default ClubPage \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/club/_layout.tsx b/frontend/sac-mobile/app/(app)/club/_layout.tsx new file mode 100644 index 000000000..b7336b730 --- /dev/null +++ b/frontend/sac-mobile/app/(app)/club/_layout.tsx @@ -0,0 +1,15 @@ +import { View, Text } from 'react-native' +import React from 'react' +import { Stack } from 'expo-router' + +const Layout = () => { + return ( + + + + + + ) +} + +export default Layout \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/club/faq/[id].tsx b/frontend/sac-mobile/app/(app)/club/faq/[id].tsx new file mode 100644 index 000000000..cd0631ab0 --- /dev/null +++ b/frontend/sac-mobile/app/(app)/club/faq/[id].tsx @@ -0,0 +1,15 @@ +import { View, Text, SafeAreaView } from 'react-native' +import React from 'react' +import { useLocalSearchParams } from 'expo-router'; + +const Faq = () => { + const { id } = useLocalSearchParams<{ id: string }>(); + + return ( + + {id} + + ) +} + +export default Faq \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/event/[id].tsx b/frontend/sac-mobile/app/(app)/event/[id].tsx index 284c54f46..43c6bdbc9 100644 --- a/frontend/sac-mobile/app/(app)/event/[id].tsx +++ b/frontend/sac-mobile/app/(app)/event/[id].tsx @@ -5,7 +5,7 @@ import { Stack, useLocalSearchParams } from 'expo-router'; import { MenuView } from '@react-native-menu/menu'; -import { HostList } from '@/app/(app)/event/_components/host-list'; +import { AllHosts } from '@/app/(app)/event/_components/all-hosts'; import ShareIcon from '@/assets/images/svg/share.svg'; import { TagList } from '@/components/all-tags'; import { Button } from '@/components/button'; @@ -14,13 +14,14 @@ import { useAuthStore } from '@/hooks/use-auth'; import { useEvent } from '@/hooks/use-event'; import { Title } from '../_components/title'; -// import { EventLocation } from './_components/event-location'; import { EventTime } from './_components/event-time'; import { HostNames } from './_components/host-names'; import { LocationView } from './_components/location-view'; +import { EventLocation } from './_components/event-location'; +import { EventHeader } from './_components/event-header'; // TODO: handle link OR location -const Event = () => { +const EventPage = () => { const { id } = useLocalSearchParams<{ id: string }>(); const { user } = useAuthStore(); @@ -59,10 +60,10 @@ const Event = () => { startTime={event.start_time} endTime={event.end_time} /> - {/* */} + /> { location={event.location} meetingLink={event.meeting_link} /> - + - - { - console.warn(JSON.stringify(nativeEvent)); - }} - actions={[ - { - id: 'yes', - title: 'Yes', - image: Platform.select({ - ios: 'checkmark', - android: 'ic_menu_save' - }) - }, - { - id: 'maybe', - title: 'Maybe', - image: Platform.select({ - ios: 'questionmark', - android: 'ic_menu_help' - }) - }, - { - id: 'no', - title: 'No', - image: Platform.select({ - ios: 'xmark', - android: 'ic_menu_delete' - }) - } - ]} - themeVariant="dark" - shouldOpenOnLongPress={false} - isAnchoredToRight={true} - > - - - - - ); -}; +export default EventPage \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/event/_components/all-hosts.tsx b/frontend/sac-mobile/app/(app)/event/_components/all-hosts.tsx new file mode 100644 index 000000000..c4bb56f82 --- /dev/null +++ b/frontend/sac-mobile/app/(app)/event/_components/all-hosts.tsx @@ -0,0 +1,34 @@ +import { FlatList, Text, View } from 'react-native'; + +import { ClubCard } from '@/components/club-card'; +import { useEventHosts } from '@/hooks/use-event'; +import { uuid } from '@/types/uuid'; + +const AllHosts = ({ eventID }: { eventID: uuid }) => { + const { data: hosts, error, isLoading } = useEventHosts(eventID); + + if (isLoading) { + return Loading...; + } + + if (error) { + return Error: {error.message}; + } + + if (!hosts || hosts.length === 0) { + return No hosts found; + } + + return ( + } + keyExtractor={(item) => item.id} + /> + ); +}; + +export { AllHosts }; \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/event/_components/event-header.tsx b/frontend/sac-mobile/app/(app)/event/_components/event-header.tsx new file mode 100644 index 000000000..695c28fdf --- /dev/null +++ b/frontend/sac-mobile/app/(app)/event/_components/event-header.tsx @@ -0,0 +1,78 @@ +import { Button } from "@/components/button"; +import { MenuView } from "@react-native-menu/menu"; +import { Share, Alert, View, Platform } from "react-native"; + +const EventHeader = () => { + const onShare = async () => { + try { + const result = await Share.share({ + message: + 'React Native | A framework for building native apps using React' + }); + if (result.action === Share.sharedAction) { + if (result.activityType) { + // shared with activity type of result.activityType + } else { + // shared + } + } else if (result.action === Share.dismissedAction) { + // dismissed + } + } catch (error: any) { + Alert.alert(error.message); + } + }; + + return ( + + + + { + console.warn(JSON.stringify(nativeEvent)); + }} + actions={[ + { + id: 'yes', + title: 'Yes', + image: Platform.select({ + ios: 'checkmark', + android: 'ic_menu_save' + }) + }, + { + id: 'maybe', + title: 'Maybe', + image: Platform.select({ + ios: 'questionmark', + android: 'ic_menu_help' + }) + }, + { + id: 'no', + title: 'No', + image: Platform.select({ + ios: 'xmark', + android: 'ic_menu_delete' + }) + } + ]} + themeVariant="dark" + shouldOpenOnLongPress={false} + isAnchoredToRight={true} + > + + + + + ); +}; + + +export { EventHeader }; \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/event/_components/event-location.tsx b/frontend/sac-mobile/app/(app)/event/_components/event-location.tsx new file mode 100644 index 000000000..2fcb247b6 --- /dev/null +++ b/frontend/sac-mobile/app/(app)/event/_components/event-location.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Linking, Text, TouchableOpacity, View } from 'react-native'; + +import Location from '@/assets/images/svg/location.svg'; + +type EventLocationProps = { + location: string; + meetingLink?: string; +}; + +const EventLocation = ({ location, meetingLink }: EventLocationProps) => { + const [city, rest] = location.split(/,(.+)/); + + return ( + + + + + {city} + , {rest} + + {meetingLink && ( + + + Linking.openURL(meetingLink!).catch((err) => + console.error('An error occurred', err) + ) + } + > + + {meetingLink} + + + + )} + + + ); +}; + +export { EventLocation }; \ No newline at end of file diff --git a/frontend/sac-mobile/app/(app)/event/_components/host-list.tsx b/frontend/sac-mobile/app/(app)/event/_components/host-list.tsx deleted file mode 100644 index eced19918..000000000 --- a/frontend/sac-mobile/app/(app)/event/_components/host-list.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ScrollView, Text, View } from 'react-native'; - -import { ClubCard } from '@/components/club-card'; -import { useEventHosts } from '@/hooks/use-event'; -import { uuid } from '@/types/uuid'; - -const HostList = ({ eventID }: { eventID: uuid }) => { - const { data: hosts, error, isLoading } = useEventHosts(eventID); - - if (isLoading) { - return Loading...; - } - - if (error) { - return Error: {error.message}; - } - - if (!hosts || hosts.length === 0) { - return No hosts found; - } - - return ( - <> - - - {hosts.map((host) => ( - - - - ))} - - - - ); -}; - -export { HostList }; diff --git a/frontend/sac-mobile/app/(app)/event/_components/host-names.tsx b/frontend/sac-mobile/app/(app)/event/_components/host-names.tsx index 635200b32..72629a9e9 100644 --- a/frontend/sac-mobile/app/(app)/event/_components/host-names.tsx +++ b/frontend/sac-mobile/app/(app)/event/_components/host-names.tsx @@ -3,6 +3,7 @@ import { Pressable, Text, View } from 'react-native'; import { useEventHosts } from '@/hooks/use-event'; import { uuid } from '@/types/uuid'; +import { router } from 'expo-router'; const HostNames = ({ eventID }: { eventID: uuid }) => { const { data: hosts, error, isLoading } = useEventHosts(eventID); @@ -27,7 +28,7 @@ const HostNames = ({ eventID }: { eventID: uuid }) => { {hosts.map((host, index) => ( console.log(`${host.name} pressed`)} + onPress={() => router.push(`/club/${host.id}`)} className={'font-bold'} > diff --git a/frontend/sac-mobile/app/(app)/event/_layout.tsx b/frontend/sac-mobile/app/(app)/event/_layout.tsx deleted file mode 100644 index 1c4a25180..000000000 --- a/frontend/sac-mobile/app/(app)/event/_layout.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { Platform, View } from 'react-native'; - -import { Stack } from 'expo-router'; - -import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { MenuView } from '@react-native-menu/menu'; - -import { LeftArrow } from '@/components/left-arrow'; - -const EventLayout = () => { - return ( - - ( - - ), - headerLeft: () => , - headerRight: () => { - return ( - { - console.warn(JSON.stringify(nativeEvent)); - }} - actions={[ - { - id: 'share', - title: 'Share Event', - image: Platform.select({ - ios: 'square.and.arrow.up', - android: 'share-variant' - }) - }, - { - id: 'report', - title: 'Report Event', - image: Platform.select({ - ios: 'person.crop.circle.badge.exclamationmark.fill', - android: 'person-circle-outline' - }) - } - ]} - > - - - ); - } - }} - /> - - ); -}; - -export default EventLayout; diff --git a/frontend/sac-mobile/components/all-tags.tsx b/frontend/sac-mobile/components/all-tags.tsx index 323520829..da007fea7 100644 --- a/frontend/sac-mobile/components/all-tags.tsx +++ b/frontend/sac-mobile/components/all-tags.tsx @@ -1,8 +1,9 @@ -import { ScrollView, Text, View } from 'react-native'; +import { ScrollView, Text, View, FlatList } from 'react-native'; import { Tag } from '@/components/tag'; import { useEventTags } from '@/hooks/use-event'; import { uuid } from '@/types/uuid'; +import { SafeAreaView } from 'react-native-safe-area-context'; // TODO: generify this component @@ -11,7 +12,7 @@ type TagListProps = { }; const TagList = ({ id }: TagListProps) => { - const { data: tags = [], isLoading, error } = useEventTags(id); + const { data: tags, isLoading, error } = useEventTags(id); if (isLoading) { return Loading...; @@ -21,24 +22,21 @@ const TagList = ({ id }: TagListProps) => { return Error: {error.message}; } - if (tags.length === 0) { - return No tags found; + if (tags?.length === 0) { + return null; } + return ( - - - {tags.map((tag) => ( - - - - ))} - - + alwaysBounceHorizontal + data={tags} + renderItem={({ item }) => } + keyExtractor={(item) => item.id} + + /> ); }; diff --git a/frontend/sac-mobile/components/club-card.tsx b/frontend/sac-mobile/components/club-card.tsx index e07ff9baa..7c15e9119 100644 --- a/frontend/sac-mobile/components/club-card.tsx +++ b/frontend/sac-mobile/components/club-card.tsx @@ -1,10 +1,11 @@ -import { Image, Text, View } from 'react-native'; +import { Image, Text, TouchableOpacity, View } from 'react-native'; import { useAssets } from 'expo-asset'; import { Club } from '@/types/club'; import { ClubContacts } from './club-contacts'; +import { Link, router } from 'expo-router'; const ClubCard = ({ club }: { club: Club }) => { const [asset, error] = useAssets( @@ -16,29 +17,31 @@ const ClubCard = ({ club }: { club: Club }) => { } return ( - - - {asset && ( - - )} - - - Club Name + router.push(`/club/${club.id}`)}> + + + {asset && ( + + )} + + + Club Name + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua... + + Read more... + + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua... - - - Read more... - - - + ); }; diff --git a/frontend/sac-mobile/components/tag.tsx b/frontend/sac-mobile/components/tag.tsx index 86e9d21ed..fabcb96c6 100644 --- a/frontend/sac-mobile/components/tag.tsx +++ b/frontend/sac-mobile/components/tag.tsx @@ -13,6 +13,7 @@ const Tag = ({ tag }: TagProps) => { onPress={() => console.log('Pressed:', tag)} size={'pill'} variant={'pill'} + className='my-1 mr-1' > {tag.name} diff --git a/frontend/sac-mobile/lib/const.ts b/frontend/sac-mobile/lib/const.ts index 838a619bd..3c3e0d8c0 100644 --- a/frontend/sac-mobile/lib/const.ts +++ b/frontend/sac-mobile/lib/const.ts @@ -1 +1 @@ -export const API_BASE_URL = 'http://localhost:8080/api/v1'; +export const API_BASE_URL = 'https://ad79-50-170-49-148.ngrok-free.app/api/v1' // 'http://localhost:8080/api/v1';