Skip to content

Commit

Permalink
feat: users are able to withdraw from an event (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
GustaveCharles authored May 23, 2024
1 parent fc05e32 commit baedbea
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 94 deletions.
12 changes: 0 additions & 12 deletions __test__/firebase/User.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,6 @@ describe("updateUserEvents", () => {
)
})

it("should not update user events if already registered", async () => {
mockGetDoc.mockResolvedValueOnce({
data: () => mockUser,
})

const uid = "123"
const eventId = "456" // Already in the events list
const result = await updateUserEvents(uid, eventId)

expect(result).toBe(false)
expect(mockSetDoc).not.toHaveBeenCalled()
})

it("should handle errors gracefully", async () => {
mockGetDoc.mockRejectedValueOnce(new Error("Firestore error"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ jest.mock("../../../../firebase/User", () => ({
updateUserEvents: jest.fn().mockResolvedValue(true)
}))

const mockGetEventData = jest.fn()

jest.mock("../../../../firebase/ManageEvents", () => ({
getEventData: jest.fn().mockResolvedValue({ uid: "123", title: "Event", location: "location", point: { x: 40.712776, y: -74.005974 }, date: new Date().toISOString(), host: "123", participants: ["123"], description: "description", imageUrl: "imageUrl" }),
getEventData: jest.fn((...args) => mockGetEventData(...args)),
updateEventData: jest.fn().mockResolvedValue(true)
}))

Expand Down Expand Up @@ -50,16 +52,35 @@ describe("ViewEventScreen", () => {
})

it("can click on participate", async () => {
const { getByText } = render(
<NavigationContainer>
<ViewEventScreen />
</NavigationContainer>
)
await waitFor(() => {
expect(getByText("Participate")).toBeTruthy()
})
const eventData = { uid: "123", title: "Event", location: "location", point: { x: 40.712776, y: -74.005974 }, date: new Date().toISOString(), host: "456", participants: ["123"], description: "description", imageUrl: "imageUrl" }
mockGetEventData.mockResolvedValueOnce(eventData)
const { getByText } = render(
<NavigationContainer>
<ViewEventScreen />
</NavigationContainer>
)
await waitFor(() => {
expect(getByText("Participate")).toBeTruthy()
})

fireEvent.press(getByText("Participate"))
fireEvent.press(getByText("Participate"))
})

it("can click on Edit", async () => {
const eventData = { uid: "123", title: "Event", location: "location", point: { x: 40.712776, y: -74.005974 }, date: new Date().toISOString(), host: "123", participants: ["123"], description: "description", imageUrl: "imageUrl" }
mockGetEventData.mockResolvedValueOnce(eventData)
const { getByText } = render(
<NavigationContainer>
<ViewEventScreen />
</NavigationContainer>
)
await waitFor(() => {
expect(getByText("Edit")).toBeTruthy()
})

fireEvent.press(getByText("Edit"))
})



})
6 changes: 2 additions & 4 deletions firebase/ManageEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,8 @@ export const updateEventData = async (eventUid: string, userId: string): Promise
const docRef = doc(db, "events", eventUid)
const docSnapshot = await getDoc(docRef)
const event = docSnapshot.data() as Event
if (event.participants.includes(userId)) {
return false
}
event.participants.push(userId)
const index = event.participants.indexOf(userId)
index === -1 ? event.participants.push(userId): event.participants.splice(index, 1)
await setDoc(docRef, event, { merge: true })
return true

Expand Down
10 changes: 3 additions & 7 deletions firebase/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,14 @@ export const updateUserEvents = async (
eventId: string
): Promise<boolean> => {
try {
//read events array and add new event
//read events array and add new event, or remove if already exists
const docRef = doc(db, "users", uid)
const user = await getDoc(docRef)
const user2 = user.data() as User
let userEvents = user2.events
if (userEvents !== undefined) {
userEvents.forEach((event) => {
if (event === eventId) {
throw new Error("You are already registered to this event.")
}
})
userEvents.push(eventId)
const index = userEvents.indexOf(eventId)
index === -1 ? userEvents.push(eventId) : userEvents.splice(index, 1)
} else {
userEvents = [eventId]
}
Expand Down
140 changes: 79 additions & 61 deletions screens/ViewDetails/ViewEventScreen/ViewEventScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { View, Text, TouchableOpacity, Alert } from 'react-native'
import { RouteProp, useRoute } from '@react-navigation/native'
import { styles } from './styles'
Expand Down Expand Up @@ -32,6 +32,7 @@ const ViewEventScreen = () => {
const [user, setUser] = useState<User | null>(null)
const [dateInISO, setDateInISO] = useState<string | null>(null)
const [host, setHost] = useState<User | null>(null)
const [participating, setParticipating] = useState<boolean>(false)

useEffect(() => {
const fetchUserData = async () => {
Expand All @@ -40,6 +41,9 @@ const ViewEventScreen = () => {
if (userId) {
const fetchedUser = await getUserData(userId)
setUser(fetchedUser)
if (fetchedUser) {
setParticipating(fetchedUser.events.includes(eventUid))
}
}
setLoading(false)
} catch (error) {
Expand Down Expand Up @@ -84,20 +88,19 @@ const ViewEventScreen = () => {
if (!user || !event) {
return
}
const eventId = await updateUserEvents(user.uid, eventUid)
const success = await updateEventData(eventUid, user.uid)
if (eventId && success) {
showSuccessToast("Successfully registered to the event.")
} else {
showErrorToast("You are already registered to this event")
const userSuccess = await updateUserEvents(user.uid, eventUid)
const eventSuccess = await updateEventData(eventUid, user.uid)
if (userSuccess && eventSuccess) {
setParticipating(!participating)
showSuccessToast(
participating
? "Successfully withdrawn from the event."
: "Successfully registered to the event."
)
}
}

if (loading || !event || !dateInISO || !host) {
console.log("loading")
console.log(event)
console.log(dateInISO)
console.log(host)
return <LoadingScreen />
}

Expand All @@ -106,67 +109,82 @@ const ViewEventScreen = () => {
<View style={viewDetailsStyles.topBackground} />
<BackArrow />

<View style={viewDetailsStyles.detailsContainer}>
<Text style={[
globalStyles.boldText,
viewDetailsStyles.title,
viewDetailsStyles.detailsText
]}>
{event.title}
</Text>
<Text style={[globalStyles.text, viewDetailsStyles.detailsText]}>
Travel - Holidays - Work
</Text>
<Text style={[globalStyles.smallText, viewDetailsStyles.detailsText]}>
{dateInISO}
</Text>
<View style={viewDetailsStyles.detailsContainer}>
<Text style={[
globalStyles.boldText,
viewDetailsStyles.title,
viewDetailsStyles.detailsText
]}>
{event.title}
</Text>
<Text style={[globalStyles.text, viewDetailsStyles.detailsText]}>
Travel - Holidays - Work
</Text>
<Text style={[globalStyles.smallText, viewDetailsStyles.detailsText]}>
{dateInISO}
</Text>

<TouchableOpacity
style={viewDetailsStyles.profileContainer}
onPress={() => { Alert.alert("Coming soon") }}>
<Text style={globalStyles.smallText}> Hosted By </Text>
<ProfilePicture
size={25}
pictureUrl=""
/>
<Text style={globalStyles.smallText} >{host.firstName} {host.lastName}</Text>
</TouchableOpacity>
<TouchableOpacity
style={viewDetailsStyles.profileContainer}
onPress={() => { Alert.alert("Coming soon") }}>
<Text style={globalStyles.smallText}> Hosted By </Text>
<ProfilePicture
size={25}
pictureUrl=""
/>
<Text style={globalStyles.smallText} >{host.firstName} {host.lastName}</Text>
</TouchableOpacity>

<View style={viewDetailsStyles.separationBar} />
<View style={styles.mapContainer}>
<MapView
style={styles.map}
initialRegion={{
<View style={viewDetailsStyles.separationBar} />
<View style={styles.mapContainer}>
<MapView
style={styles.map}
initialRegion={{
latitude: event.point.x,
longitude: event.point.y,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
showsUserLocation
showsMyLocationButton
provider={PROVIDER_GOOGLE}
>
<Marker
title={event.title}
coordinate={{
latitude: event.point.x,
longitude: event.point.y,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
showsUserLocation
showsMyLocationButton
provider={PROVIDER_GOOGLE}
>
<Marker
title={event.title}
coordinate={{
latitude: event.point.x,
longitude: event.point.y,
}}
/>
</MapView>
</View>
/>
</MapView>
</View>

<Text style={[globalStyles.smallText, viewDetailsStyles.descriptionContainer]}>
{event.description}
</Text>
<Text style={[globalStyles.smallText, viewDetailsStyles.descriptionContainer]}>
{event.description}
</Text>
{user?.uid !== event.host && (
<TouchableOpacity
key={participating ? "withdraw" : "participate"}
style={styles.participateButton}
onPress={() => registerToEvent()}>
onPress={registerToEvent}
>
<Text style={globalStyles.boldText}>
Participate
{participating ? "Withdraw" : "Participate"}

</Text>
</TouchableOpacity>
</View>
)}

{user?.uid === event.host && (
<TouchableOpacity
style={styles.participateButton}
onPress={() => Alert.alert("Coming soon")}
>
<Text style={globalStyles.boldText}>Edit</Text>
</TouchableOpacity>
)
}
</View>
</View>
)
}
Expand Down

0 comments on commit baedbea

Please # to comment.