Skip to content

Commit

Permalink
Merge pull request #41 from unibuc-cs/qr-code
Browse files Browse the repository at this point in the history
Qr code
  • Loading branch information
biancaisc authored Feb 2, 2025
2 parents 6013492 + 17d213a commit 8ef8703
Show file tree
Hide file tree
Showing 13 changed files with 1,493 additions and 10 deletions.
10 changes: 9 additions & 1 deletion CollectifyFrontend/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@
"backgroundColor": "#ffffff"
}
],
"expo-secure-store"
"expo-secure-store",
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"recordAudioAndroid": true
}
]
],
"experiments": {
"typedRoutes": true
Expand Down
13 changes: 13 additions & 0 deletions CollectifyFrontend/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ export default function TabLayout() {
),
}}
/>

<Tabs.Screen
name="scan"
options={{
title: "Scan QR",
headerRight: () => (
<HeaderProfileButton refreshLoginStatus={checkLoginStatus} />
),
tabBarIcon: ({ color, focused }) => (
<FontAwesome name={focused ? 'file' : 'file-o'} color={color} size={27} />
),
}}
/>
</Tabs>
);
}
Expand Down
119 changes: 119 additions & 0 deletions CollectifyFrontend/app/(tabs)/scan.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Alert, Button as RNButton } from 'react-native';
import { Camera, CameraView } from 'expo-camera';
import { useRouter } from 'expo-router';
import api from '../../services/axiosInstance'; // Ensure this points to your API setup
import { getProfile } from '../../services/authService';
import { getGroupMembers } from '../../services/groupService';

const ScanScreen = () => {
const router = useRouter();
const [groupId, setGroupId] = useState<string | null>(null);
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
const [loading, setLoading] = useState<boolean>(false); // Loading state

useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);

// Reset groupId and loading when navigating back to allow a new scan.
useEffect(() => {
setGroupId(null);
setLoading(false);
}, [router]);

const handleBarcodeScanned = async ({ data }: { data: string }) => {
setGroupId(data); // QR Code contains groupId
setLoading(true); // Set loading to true while processing

try {
const user = await getProfile(); // Get current user ID
if (!user || !user.id) {
Alert.alert("Error", "User not authenticated.");
setLoading(false);
return;
}

// Check if the user is already in the group using the scanned groupId directly
const response = await getGroupMembers(data);
// Extract members from the response (wrapped in $values)
const members: Array<{ id: string }> = response?.$values ?? [];
const isUserInGroup = members.some(member => member.id === user.id);

if (isUserInGroup) {
Alert.alert("Already in the group", "You are already a member of this group.");
router.push('/(tabs)/groups');
setLoading(false);
return;
}

// If not, proceed to add the user
const addResponse = await api.post('/api/groups/add_member', {
groupId: data,
memberId: user.id,
});

setLoading(false);

if (addResponse.status === 200) {
Alert.alert("Success", "You have been added to the group!");
router.push('/(tabs)/groups');
} else {
Alert.alert("Error", "Failed to join group.");
}
} catch (error: any) {
setLoading(false);
console.error("Error joining group:", error.response || error);
Alert.alert("Error", "An error occurred.");
}
};

if (hasPermission === null) {
return <Text>Requesting permission...</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}

return (
<View style={styles.container}>
{groupId === null ? (
<CameraView
onBarcodeScanned={handleBarcodeScanned}
style={StyleSheet.absoluteFillObject}
/>
) : (
<View style={styles.messageContainer}>
<Text style={styles.messageText}>
{loading ? "Joining group..." : "Successfully added to group!"}
</Text>
{/* Add a button to reset state and allow re-scanning */}
<RNButton title="Scan Again" onPress={() => {
setGroupId(null);
setLoading(false);
}} />
</View>
)}
</View>
);
};

export default ScanScreen;

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
messageContainer: {
alignItems: 'center',
},
messageText: {
fontSize: 18,
marginBottom: 10,
},
});
14 changes: 13 additions & 1 deletion CollectifyFrontend/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="(tabs)"
options={{
headerShown: false
}} />

<Stack.Screen
name="profile"
Expand Down Expand Up @@ -48,6 +52,14 @@ export default function RootLayout() {
title: "Notes"
}}
/>

<Stack.Screen
name = "(tabs)/scan"
options={{
title: "Scan QR"
}}
/>

</Stack>
);
}
63 changes: 62 additions & 1 deletion CollectifyFrontend/app/groups/[item].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Note = {
export default function GroupNotesScreen() {
const router = useRouter();
const { item: groupId } = useLocalSearchParams<{ item: string }>();
//console.log("THIS IS THE GROUP ID from Ioana:", groupId); // Log to verify if groupId is valid

const [groupName, setGroupName] = useState<string>(''); // State for group name
const [notes, setNotes] = useState<Note[]>([]); // State for notes
const [error, setError] = useState<string | null>(null); // State for errors
Expand Down Expand Up @@ -130,14 +132,41 @@ export default function GroupNotesScreen() {
}}
placeholder="Group Name"
/>

{/* Button Group */}
<View style={styles.buttonGroup}>
{/* QR Code Button */}
<Button
status="info"
onPress={() => router.push({
pathname: '../qrCode',
params: { item:groupId } // Pass the groupId as a parameter
})}
style={styles.qrButton}
>
Show QR Code
</Button>

{/* Members Button */}
<Button
status="primary"
onPress={() => router.push({
pathname: './groupmembers',
params: { item:groupId } // Pass the groupId as a parameter
})}
style={styles.membersButton}
>
See Members
</Button>
</View>

{/* Notes List */}
<List
data={notes}
renderItem={renderNote}
keyExtractor={(note) => note.id.toString()} // Ensure note.id is a string
style={styles.list}
/>
/>
<Button
status="warning"
onPress={handleAddNote}
Expand All @@ -152,6 +181,38 @@ export default function GroupNotesScreen() {
}

const styles = StyleSheet.create({
buttonGroup: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 10,
},
qrButton: {
borderRadius: 8,
paddingVertical: 8, // Slightly smaller vertical padding
paddingHorizontal: 12, // Slightly smaller horizontal padding
backgroundColor: 'rgba(255, 255, 255, 0.7)', // Light transparent background
borderColor: '#ccc', // Light border
borderWidth: 1, // Border width
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 3.84,
flex: 1,
marginRight: 10,
},
membersButton: {
borderRadius: 8,
paddingVertical: 8, // Slightly smaller vertical padding
paddingHorizontal: 12, // Slightly smaller horizontal padding
backgroundColor: 'rgba(255, 255, 255, 0.7)', // Light transparent background
borderColor: '#ccc', // Light border
borderWidth: 1, // Border width
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 3.84,
flex: 1,
},
container: {
flex: 1,
padding: 20,
Expand Down
Loading

0 comments on commit 8ef8703

Please sign in to comment.