diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f8fef7e..2461564 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + { + return listOf(ContactsModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/android/app/src/main/java/com/circlecollect/MainApplication.kt b/android/app/src/main/java/com/circlecollect/MainApplication.kt index f4394e6..e411077 100644 --- a/android/app/src/main/java/com/circlecollect/MainApplication.kt +++ b/android/app/src/main/java/com/circlecollect/MainApplication.kt @@ -9,6 +9,7 @@ import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost +import com.circlecollect.ContactsPackage class MainApplication : Application(), ReactApplication { @@ -18,6 +19,7 @@ class MainApplication : Application(), ReactApplication { PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()) + add(ContactsPackage()) } override fun getJSMainModuleName(): String = "index" diff --git a/src/components/RequestCard.tsx b/src/components/RequestCard.tsx index 57eb7a2..b0ac2fd 100644 --- a/src/components/RequestCard.tsx +++ b/src/components/RequestCard.tsx @@ -23,7 +23,7 @@ export const RequestCard: FC = ({ item, onApprove, onDecline } /> {item.userName} - {item.mobile} + {item.mobile} @@ -37,13 +37,13 @@ export const RequestCard: FC = ({ item, onApprove, onDecline } {/* Bottom section with action buttons */} onDecline(item)}> - - Decline + + Decline onApprove(item)}> {/* Corrected icon color and updated text color for better contrast */} - - Approve + + Approve @@ -77,7 +77,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, - borderBottomColor: colors.border, + borderBottomColor: colors.textLight, paddingBottom: 12, }, avatar: { @@ -125,7 +125,7 @@ const styles = StyleSheet.create({ borderRadius: 12, }, declineButton: { - backgroundColor: colors.lightDanger, + backgroundColor: colors.pastelPink, }, approveButton: { // Set background color as requested diff --git a/src/fetchContacts.ts b/src/fetchContacts.ts new file mode 100644 index 0000000..b6b1617 --- /dev/null +++ b/src/fetchContacts.ts @@ -0,0 +1,29 @@ +import { PermissionsAndroid, Platform, NativeModules } from "react-native"; + +const { ContactsModule } = NativeModules; + +async function fetchContacts() { + if (Platform.OS === "android") { + const granted = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.READ_CONTACTS, + { + title: "Contacts Permission", + message: "This app would like to view your contacts.", + buttonPositive: "OK", + } + ); + + if (granted !== PermissionsAndroid.RESULTS.GRANTED) { + console.log("Contacts permission denied"); + return; + } + } + + try { + const contacts = await ContactsModule.getContacts(); + console.log("Contacts:", contacts); + } catch (error) { + console.error("Error fetching contacts:", error); + } +} +export { fetchContacts }; \ No newline at end of file diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 2da5c4a..7390d57 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -12,6 +12,7 @@ import GroupReview from "../screens/GroupReview"; import BottomTabs from "./BottomTabs"; import BecomeOrg from "../screens/profile/BecomeOrg"; import EditGroupScreen from "../screens/management/EditGroupScreen"; +import YourContacts from "../screens/management/YourContacts"; const Stack = createNativeStackNavigator(); export type RootStackParamList = { @@ -62,7 +63,8 @@ export default function AppNavigator() { - + + {/* */} ); diff --git a/src/screens/OtpVarificationScreen.tsx b/src/screens/OtpVarificationScreen.tsx index 17f78a4..d2a03e7 100644 --- a/src/screens/OtpVarificationScreen.tsx +++ b/src/screens/OtpVarificationScreen.tsx @@ -111,7 +111,7 @@ export default function OtpVerificationScreen() { { headers: { "Content-Type": "application/json", Accept: "*/*" } } ); - console.log("📩 OTP Resent:", res.data); + console.log("📩 OTP Resent:", res); setCode(Array(CELL_COUNT).fill("")); inputs.current[0]?.focus(); ToastAndroid.show("OTP Sent", ToastAndroid.SHORT); diff --git a/src/screens/management/ManagementScreen.tsx b/src/screens/management/ManagementScreen.tsx index ec59644..e3bb4c1 100644 --- a/src/screens/management/ManagementScreen.tsx +++ b/src/screens/management/ManagementScreen.tsx @@ -24,6 +24,7 @@ import { MyGroupCard } from "../../components/MyGroupCard"; import { JoinedGroupCard } from "../../components/JoinedGroupCard"; import { RequestCard } from "../../components/RequestCard"; import { useSelector } from "react-redux"; +import Ionicons from "@react-native-vector-icons/ionicons"; type GroupManagementNavProp = StackNavigationProp; type ActiveTab = "My Groups" | "Joined Groups" | "Requests" | "Your Requests"; @@ -172,6 +173,32 @@ const ManagementScreen: FC = () => { ))} + { + activeTab === "Requests" && userRole === "ORGANIZER" ? ( + navigation.navigate("YourContacts" as never)} + > + + + ) : null + } + {renderContent()} diff --git a/src/screens/management/YourContacts.tsx b/src/screens/management/YourContacts.tsx new file mode 100644 index 0000000..d256c99 --- /dev/null +++ b/src/screens/management/YourContacts.tsx @@ -0,0 +1,274 @@ + + +import React, { useState, useEffect } from "react"; +import { + StyleSheet, + Text, + View, + FlatList, + TouchableOpacity, + Modal, + PermissionsAndroid, + Platform, + NativeModules, + Linking, + Alert, +} from "react-native"; +import AppView from "../../components/AppView"; + +const { ContactsModule } = NativeModules; + +const APP_URL = "https://play.google.com/store/apps/details?id=com.yourapp"; // replace with your real app url + +const YourContacts = () => { + const [contacts, setContacts] = useState([]); + const [selectedContact, setSelectedContact] = useState(null); + const [modalVisible, setModalVisible] = useState(false); + + useEffect(() => { + handleFetchContacts(); + }, []); + + const handleFetchContacts = async () => { + try { + if (Platform.OS === "android") { + const granted = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.READ_CONTACTS, + { + title: "Contacts Permission", + message: "This app would like to view your contacts.", + buttonPositive: "OK", + } + ); + + if (granted !== PermissionsAndroid.RESULTS.GRANTED) { + console.log("Contacts permission denied"); + return; + } + } + + const fetchedContacts = await ContactsModule.getContacts(); + setContacts(fetchedContacts); + } catch (error) { + console.error("Error fetching contacts:", error); + } + }; + + const APP_URL = "https://play.google.com/store/apps/details?id=com.yourapp"; // your app link + const WHATSAPP_URL = "https://play.google.com/store/apps/details?id=com.whatsapp"; // fallback store link + + const handleSend = async (contact: any) => { + if (!contact) return; + + const message = `Hey ${contact.name}, check out this awesome app! Download here: ${APP_URL}`; + + // direct send to contact if you want + const url = `whatsapp://send?phone=${contact.phone}&text=${encodeURIComponent( + message + )}`; + + try { + const supported = await Linking.canOpenURL("whatsapp://send"); + if (supported) { + // WhatsApp installed + await Linking.openURL(url); + } else { + // WhatsApp not installed → send user to Play Store/App Store + if (Platform.OS === "android") { + await Linking.openURL(WHATSAPP_URL); + } else if (Platform.OS === "ios") { + await Linking.openURL("itms-apps://apps.apple.com/app/whatsapp-messenger/id310633997"); + } + } + } catch (err) { + console.error("Error sending to WhatsApp:", err); + Alert.alert("Error", "Something went wrong while trying to open WhatsApp"); + } + + setModalVisible(false); + }; + + + const renderContact = ({ item }: { item: any }) => ( + + + + {item.name ? item.name.charAt(0).toUpperCase() : "?"} + + + + {item.name} + {item.phone} + + { + setSelectedContact(item); + setModalVisible(true); + }} + > + Invite + + + ); + + return ( + + + Select Contact + + + + index.toString()} + renderItem={renderContact} + /> + + {/* Send Modal */} + setModalVisible(false)} + > + + + Send Invite + {selectedContact && ( + + Send invite to {selectedContact.name} ({selectedContact.phone}) + + )} + + setModalVisible(false)} + > + Cancel + + handleSend(selectedContact)} + > + Send + + + + + + + ); +}; + + +export default YourContacts; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#fff", + paddingHorizontal: 10, + paddingTop: 20, + }, + header: { + fontSize: 20, + fontWeight: "bold", + marginBottom: 15, + }, + contactRow: { + flexDirection: "row", + alignItems: "center", + paddingVertical: 12, + borderBottomWidth: 0.5, + borderBottomColor: "#ddd", + }, + avatar: { + width: 45, + height: 45, + borderRadius: 25, + backgroundColor: "#25D366", + justifyContent: "center", + alignItems: "center", + marginRight: 12, + }, + avatarText: { + color: "#fff", + fontSize: 18, + fontWeight: "bold", + }, + contactInfo: { + flex: 1, + }, + contactName: { + fontSize: 16, + fontWeight: "600", + }, + contactPhone: { + fontSize: 13, + color: "gray", + }, + inviteButton: { + paddingHorizontal: 14, + paddingVertical: 6, + borderRadius: 8, + borderWidth: 1, + borderColor: "#25D366", + }, + inviteText: { + color: "#25D366", + fontWeight: "bold", + }, + modalOverlay: { + flex: 1, + backgroundColor: "rgba(0,0,0,0.5)", + justifyContent: "center", + alignItems: "center", + }, + modalContent: { + width: "80%", + backgroundColor: "#fff", + borderRadius: 10, + padding: 20, + alignItems: "center", + }, + modalTitle: { + fontSize: 18, + fontWeight: "bold", + marginBottom: 10, + }, + modalText: { + fontSize: 15, + marginBottom: 20, + textAlign: "center", + }, + modalActions: { + flexDirection: "row", + justifyContent: "space-between", + width: "100%", + }, + cancelButton: { + flex: 1, + marginRight: 10, + paddingVertical: 10, + borderRadius: 8, + backgroundColor: "#ddd", + alignItems: "center", + }, + cancelText: { + fontWeight: "bold", + color: "#333", + }, + sendButton: { + flex: 1, + marginLeft: 10, + paddingVertical: 10, + borderRadius: 8, + backgroundColor: "#25D366", + alignItems: "center", + }, + sendText: { + fontWeight: "bold", + color: "#fff", + }, +}); diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index d33d621..97df1a7 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -23,6 +23,7 @@ import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { ApiResponse } from "../home/UserProfileStatus"; import { BASE_URL } from "../../utils/ApiUrls"; import AppView from "../../components/AppView"; +import { useSelector } from "react-redux"; @@ -47,6 +48,7 @@ type UserProfile = { const Profile = () => { const navigation = useNavigation(); + const user1 = useSelector((state: any) => state.user); const [user, setUser] = useState(null); const [fetching, setFetching] = useState(true); const [editing, setEditing] = useState(false); @@ -222,7 +224,7 @@ const Profile = () => { const response = await fetch(`${BASE_URL}user/profile/status`, { method: "GET", headers: { - Accept: "/", + Accept: "*/*", Authorization: `Bearer ${token}`, }, }); @@ -232,6 +234,7 @@ const Profile = () => { console.log("👤 Profile Status Response:", json); if (json.status === "success") { setStatusData(json.data); + console.log("statusData response :", json); } else { setError(json.message || "Failed to fetch status"); @@ -273,6 +276,8 @@ const Profile = () => { ); }; + console.log("statusData?.role :", statusData?.role); + console.log("statusData?.organizerStatus :", statusData?.organizerStatus); useFocusEffect( useCallback(() => { @@ -327,15 +332,28 @@ const Profile = () => { {/* Profile Card */} {/* Profile Image Section */} - {user?.role !== "USER" - && - (user?.role === "ORGANIZER" && - (user?.organizerStatus === "NOT_APPLIED" || user?.organizerStatus !== "PENDING")) ? ( - Your Role: {user.role} | Organizer Status: {user.organizerStatus} - ) : ( - - - )} + { + statusData?.role === "USER" && statusData?.organizerStatus !== "NOT_APPLIED" ? ( + + Your Role: {statusData.role} | Organizer Status: {statusData.organizerStatus} + + ) : ( + statusData?.role !== "ORGANIZER" && ( + + ) + ) + } + = ({ role, status }) => { if (newValue) { navigation.navigate("BecomeOrg"); // Navigate when ON - setTimeout(() => { - setIsEnabled(false); - }, 2000); - // Reset switch to OFF after navigation } else { navigation.navigate("Profile"); // Navigate when OFF } @@ -40,18 +35,17 @@ const SwitchNavigator: React.FC = ({ role, status }) => { return ( - {!isEnabled && role !== "USER" + {!isEnabled && role !== "USER" ? "Become an Organizer" : `Request Organizer Role ${status}`} - {role !== "USER" ? ( - ) : } + ); };