├── .gitignore ├── App.js ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── home-image.jpg ├── icon.png └── splash.png ├── babel.config.js ├── components ├── Chatcomponent.js ├── Messagecomponent.js └── Modal.js ├── context └── index.js ├── package-lock.json ├── package.json ├── screens ├── Chatscreen.js ├── Homescreen.js └── Messagescreen.js └── utils └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # Temporary files created by Metro to check the health of the file watcher 17 | .metro-health-check* 18 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from "expo-status-bar"; 2 | import { StyleSheet } from "react-native"; 3 | import Homescreen from "./screens/Homescreen"; 4 | import Chatscreen from "./screens/Chatscreen"; 5 | import Messagescreen from "./screens/Messagescreen"; 6 | 7 | import { NavigationContainer } from "@react-navigation/native"; 8 | import { createNativeStackNavigator } from "@react-navigation/native-stack"; 9 | import GlobalState from "./context"; 10 | 11 | const Stack = createNativeStackNavigator(); 12 | 13 | export default function App() { 14 | return ( 15 | 16 | 17 | 18 | {/* all the screens here */} 19 | 24 | 29 | 30 | 31 | 32 | 34 | ); 35 | } 36 | 37 | const styles = StyleSheet.create({ 38 | container: { 39 | flex: 1, 40 | backgroundColor: "#fff", 41 | alignItems: "center", 42 | justifyContent: "center", 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "frontend", 4 | "slug": "frontend", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": [ 15 | "**/*" 16 | ], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#ffffff" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/React-Native-Chat-App-Frontend/d950f30d26d244c78d7aceba80325708b6013afd/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/React-Native-Chat-App-Frontend/d950f30d26d244c78d7aceba80325708b6013afd/assets/favicon.png -------------------------------------------------------------------------------- /assets/home-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/React-Native-Chat-App-Frontend/d950f30d26d244c78d7aceba80325708b6013afd/assets/home-image.jpg -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/React-Native-Chat-App-Frontend/d950f30d26d244c78d7aceba80325708b6013afd/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/React-Native-Chat-App-Frontend/d950f30d26d244c78d7aceba80325708b6013afd/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /components/Chatcomponent.js: -------------------------------------------------------------------------------- 1 | import { Pressable, StyleSheet, Text, View } from "react-native"; 2 | import { FontAwesome } from "@expo/vector-icons"; 3 | import { useContext, useEffect } from "react"; 4 | import { GlobalContext } from "../context"; 5 | import { useNavigation } from "@react-navigation/native"; 6 | 7 | export default function Chatcomponent({ item }) { 8 | const navigation = useNavigation(); 9 | 10 | console.log(item.messages[item.messages.length - 1]); 11 | 12 | 13 | 14 | function handleNavigateToMessageScreen() { 15 | navigation.navigate("Messagescreen", { 16 | currentGroupName: item.currentGroupName, 17 | currentGroupID: item.id, 18 | }); 19 | } 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | {item.currentGroupName} 29 | 30 | {item && item.messages && item.messages.length ? item.messages[item.messages.length - 1].text : "Tap to start messaging"} 31 | 32 | 33 | 34 | 35 | {item && item.messages && item.messages.length ? item.messages[item.messages.length - 1].time : "Now"} 36 | 37 | 38 | 39 | 40 | ); 41 | } 42 | 43 | const styles = StyleSheet.create({ 44 | chat: { 45 | width: "100%", 46 | flexDirection: "row", 47 | alignItems: "center", 48 | borderRadius: 5, 49 | padding: 10, 50 | backgroundColor: "#fff", 51 | height: 80, 52 | marginBottom: 10, 53 | }, 54 | userName: { 55 | fontSize: 18, 56 | marginBottom: 5, 57 | fontWeight: "bold", 58 | }, 59 | message: { 60 | fontSize: 14, 61 | opacity: 0.8, 62 | }, 63 | rightContainer: { 64 | flexDirection: "row", 65 | justifyContent: "space-between", 66 | flex: 1, 67 | }, 68 | time: { 69 | opacity: 0.6, 70 | }, 71 | circle: { 72 | width: 50, 73 | borderRadius: 50, 74 | height: 50, 75 | alignItems: "center", 76 | justifyContent: "center", 77 | borderWidth: 2, 78 | marginRight: 10, 79 | }, 80 | }); 81 | -------------------------------------------------------------------------------- /components/Messagecomponent.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from "react-native"; 2 | 3 | export default function Messagecomponent({ currentUser, item }) { 4 | const currentUserStatus = item.currentUser !== currentUser; 5 | 6 | console.log(currentUserStatus , item); 7 | 8 | return ( 9 | 10 | 11 | 12 | 19 | 24 | {item.text} 25 | 26 | 27 | 28 | {item.time} 29 | 30 | 31 | ); 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | messageItemWrapper: { 36 | maxWidth: "50%", 37 | marginBottom: 15, 38 | }, 39 | messageItemInnerWrapper: { 40 | flexDirection: "row", 41 | alignItems: "center", 42 | }, 43 | messageItem: { 44 | width: "100%", 45 | backgroundColor: "#ffffff", 46 | padding: 20, 47 | borderRadius: 10, 48 | marginBottom: 2, 49 | }, 50 | messageTime : { 51 | marginLeft : 10 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /components/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { 3 | Alert, 4 | Modal, 5 | StyleSheet, 6 | Text, 7 | Pressable, 8 | View, 9 | TextInput, 10 | StatusBar, 11 | Keyboard, 12 | } from "react-native"; 13 | import { GlobalContext } from "../context"; 14 | import { socket } from "../utils"; 15 | 16 | const NewGroupModal = () => { 17 | const { 18 | modalVisible, 19 | setModalVisible, 20 | currentGroupName, 21 | setCurrentGroupName, 22 | } = useContext(GlobalContext); 23 | 24 | function handleCreateNewRoom() { 25 | console.log(currentGroupName); 26 | socket.emit("createNewGroup", currentGroupName); 27 | setModalVisible(false); 28 | setCurrentGroupName(""); 29 | Keyboard.dismiss(); 30 | } 31 | 32 | return ( 33 | { 38 | Alert.alert("Modal has been closed."); 39 | setModalVisible(!modalVisible); 40 | }} 41 | > 42 | 43 | 44 | setCurrentGroupName(value)} 49 | value={currentGroupName} 50 | /> 51 | 52 | 53 | 54 | Add 55 | 56 | 57 | setModalVisible(false)} 59 | style={styles.button} 60 | > 61 | 62 | Cancel 63 | 64 | 65 | 66 | 67 | 68 | 69 | ); 70 | }; 71 | 72 | const styles = StyleSheet.create({ 73 | centeredView: { 74 | flex: 1, 75 | justifyContent: "center", 76 | alignItems: "center", 77 | }, 78 | modalView: { 79 | margin: 20, 80 | backgroundColor: "white", 81 | borderRadius: 20, 82 | padding: 35, 83 | // alignItems: "center", 84 | shadowColor: "#000", 85 | shadowOffset: { 86 | width: 0, 87 | height: 2, 88 | }, 89 | shadowOpacity: 0.25, 90 | shadowRadius: 4, 91 | elevation: 5, 92 | }, 93 | textStyle: { 94 | color: "white", 95 | fontWeight: "bold", 96 | textAlign: "center", 97 | }, 98 | modalText: { 99 | marginBottom: 15, 100 | textAlign: "center", 101 | }, 102 | loginInput: { 103 | borderRadius: 50, 104 | borderWidth: 1, 105 | padding: 8, 106 | }, 107 | button: { 108 | backgroundColor: "#703efe", 109 | padding: 15, 110 | marginVertical: 10, 111 | elevation: 1, 112 | borderRadius: 50, 113 | }, 114 | buttonWrapper: { 115 | flexDirection: "row", 116 | gap: 10, 117 | }, 118 | buttonText: { 119 | textAlign: "center", 120 | color: "#fff", 121 | fontWeight: "bold", 122 | fontSize: 15, 123 | }, 124 | }); 125 | 126 | export default NewGroupModal; 127 | -------------------------------------------------------------------------------- /context/index.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | 3 | export const GlobalContext = createContext(null); 4 | 5 | function GlobalState({ children }) { 6 | const [showLoginView, setShowLoginView] = useState(false); 7 | const [currentUserName, setCurrentUserName] = useState(""); 8 | const [currentUser, setCurrentUser] = useState(""); 9 | const [allUsers, setAllUsers] = useState([]); 10 | const [allChatRooms, setAllChatRooms] = useState([]); 11 | const [modalVisible, setModalVisible] = useState(false); 12 | const [currentGroupName, setCurrentGroupName] = useState(""); 13 | const [allChatMessages, setAllChatMessages] = useState([]); 14 | const [currentChatMesage, setCurrentChatMessage] = useState('') 15 | 16 | return ( 17 | 38 | {children} 39 | 40 | ); 41 | } 42 | 43 | export default GlobalState; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "@react-navigation/native": "^6.1.6", 13 | "@react-navigation/native-stack": "^6.9.12", 14 | "expo": "~48.0.18", 15 | "expo-status-bar": "~1.4.4", 16 | "react": "18.2.0", 17 | "react-native": "0.71.8", 18 | "react-native-safe-area-context": "^4.5.3", 19 | "react-native-screens": "^3.21.0", 20 | "socket.io-client": "^4.6.2" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.20.0" 24 | }, 25 | "private": true 26 | } 27 | -------------------------------------------------------------------------------- /screens/Chatscreen.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from "react"; 2 | import { 3 | FlatList, 4 | Pressable, 5 | StatusBar, 6 | StyleSheet, 7 | Text, 8 | View, 9 | } from "react-native"; 10 | import { GlobalContext } from "../context"; 11 | import { AntDesign } from "@expo/vector-icons"; 12 | import Chatcomponent from "../components/Chatcomponent"; 13 | import NewGroupModal from "../components/Modal"; 14 | import { socket } from "../utils"; 15 | export default function Chatscreen({ navigation }) { 16 | const { 17 | currentUser, 18 | allChatRooms, 19 | setAllChatRooms, 20 | modalVisible, 21 | setModalVisible, 22 | setCurrentUser, 23 | setShowLoginView, 24 | } = useContext(GlobalContext); 25 | 26 | useEffect(() => { 27 | socket.emit("getAllGroups"); 28 | 29 | socket.on("groupList", (groups) => { 30 | console.log(groups ,'hhhhhhhhhhhhhhhhhhhhhhh'); 31 | setAllChatRooms(groups); 32 | }); 33 | }, [socket]); 34 | 35 | function handleLogout() { 36 | setCurrentUser(""); 37 | setShowLoginView(false); 38 | } 39 | 40 | useEffect(() => { 41 | if (currentUser.trim() === "") navigation.navigate("Homescreen"); 42 | }, [currentUser]); 43 | 44 | return ( 45 | 46 | 47 | 48 | Welcome {currentUser}! 49 | 50 | 51 | 52 | 53 | 54 | 55 | {allChatRooms && allChatRooms.length > 0 ? ( 56 | } 59 | keyExtractor={(item) => item.id} 60 | /> 61 | ) : null} 62 | 63 | 64 | setModalVisible(true)} style={styles.button}> 65 | 66 | Create New Group 67 | 68 | 69 | 70 | {modalVisible && } 71 | 72 | ); 73 | } 74 | 75 | const styles = StyleSheet.create({ 76 | mainWrapper: { 77 | backgroundColor: "#eee", 78 | flex: 1, 79 | }, 80 | topContainer: { 81 | backgroundColor: "#fff", 82 | height: 70, 83 | width: "100%", 84 | padding: 20, 85 | justifyContent: "center", 86 | marginBottom: 15, 87 | flex: 0.3, 88 | }, 89 | header: { 90 | flexDirection: "row", 91 | alignItems: "center", 92 | justifyContent: "center", 93 | }, 94 | heading: { 95 | fontSize: 30, 96 | fontWeight: "bold", 97 | textDecorationLine: "underline", 98 | }, 99 | listContainer: { 100 | flex: 3.4, 101 | paddingHorizontal: 10, 102 | }, 103 | bottomContainer: { 104 | flex: 0.3, 105 | padding: 10, 106 | }, 107 | button: { 108 | backgroundColor: "#703efe", 109 | padding: 12, 110 | width: "100%", 111 | elevation: 1, 112 | borderRadius: 50, 113 | }, 114 | buttonText: { 115 | textAlign: "center", 116 | color: "#fff", 117 | fontWeight: "bold", 118 | fontSize: 20, 119 | }, 120 | }); 121 | -------------------------------------------------------------------------------- /screens/Homescreen.js: -------------------------------------------------------------------------------- 1 | import { 2 | Alert, 3 | ImageBackground, 4 | Keyboard, 5 | Pressable, 6 | StyleSheet, 7 | Text, 8 | TextInput, 9 | View, 10 | } from "react-native"; 11 | import homeImage from "../assets/home-image.jpg"; 12 | import { useContext, useEffect } from "react"; 13 | import { GlobalContext } from "../context"; 14 | 15 | export default function Homescreen({ navigation }) { 16 | const { 17 | showLoginView, 18 | setShowLoginView, 19 | currentUserName, 20 | setCurrentUserName, 21 | currentUser, 22 | setCurrentUser, 23 | allUsers, 24 | setAllUsers, 25 | } = useContext(GlobalContext); 26 | 27 | function handleRegisterAndSignIn(isLogin) { 28 | if (currentUserName.trim() !== "") { 29 | const index = allUsers.findIndex( 30 | (userItem) => userItem === currentUserName 31 | ); 32 | 33 | if (isLogin) { 34 | if (index === -1) { 35 | Alert.alert("Please register first"); 36 | } else { 37 | setCurrentUser(currentUserName); 38 | } 39 | } else { 40 | if (index === -1) { 41 | allUsers.push(currentUserName); 42 | setAllUsers(allUsers); 43 | setCurrentUser(currentUserName); 44 | } else { 45 | Alert.alert("Already registered ! Please login"); 46 | } 47 | } 48 | 49 | setCurrentUserName(""); 50 | } else { 51 | Alert.alert("User name field is empty"); 52 | } 53 | 54 | Keyboard.dismiss(); 55 | } 56 | 57 | useEffect(() => { 58 | if (currentUser.trim() !== "") navigation.navigate("Chatscreen"); 59 | }, [currentUser]); 60 | 61 | console.log(allUsers, currentUser); 62 | 63 | return ( 64 | 65 | 66 | 67 | {showLoginView ? ( 68 | 69 | 70 | Enter Your User Name 71 | setCurrentUserName(value)} 76 | value={currentUserName} 77 | /> 78 | 79 | 80 | handleRegisterAndSignIn(false)} 82 | style={styles.button} 83 | > 84 | 85 | Register 86 | 87 | 88 | handleRegisterAndSignIn(true)} 90 | style={styles.button} 91 | > 92 | 93 | Login 94 | 95 | 96 | 97 | 98 | ) : ( 99 | 100 | Connect , Grow and Inspire 101 | 102 | Connect people around the world for free 103 | 104 | setShowLoginView(true)} 107 | > 108 | 109 | Get Started 110 | 111 | 112 | 113 | )} 114 | 115 | 116 | ); 117 | } 118 | 119 | const styles = StyleSheet.create({ 120 | mainWrapper: { 121 | flex: 1, 122 | }, 123 | homeImage: { 124 | width: "100%", 125 | flex: 3, 126 | justifyContent: "center", 127 | }, 128 | content: { 129 | flex: 1, 130 | alignItems: "center", 131 | justifyContent: "center", 132 | width: "100%", 133 | backgroundColor: "#fff", 134 | }, 135 | infoBlock: { 136 | width: "100%", 137 | alignItems: "center", 138 | justifyContent: "center", 139 | }, 140 | heading: { 141 | fontSize: 28, 142 | fontWeight: "bold", 143 | color: "#000", 144 | marginBottom: 10, 145 | }, 146 | subHeading: { 147 | fontSize: 15, 148 | color: "#acacac", 149 | marginBottom: 15, 150 | }, 151 | loginInput: { 152 | borderRadius: 50, 153 | borderWidth: 1, 154 | padding: 8, 155 | }, 156 | button: { 157 | backgroundColor: "#703efe", 158 | padding: 15, 159 | marginVertical: 10, 160 | width: "34%", 161 | elevation: 1, 162 | borderRadius: 50, 163 | }, 164 | buttonWrapper: { 165 | flexDirection: "row", 166 | gap: 10, 167 | }, 168 | buttonText: { 169 | textAlign: "center", 170 | color: "#fff", 171 | fontWeight: "bold", 172 | fontSize: 15, 173 | }, 174 | }); 175 | -------------------------------------------------------------------------------- /screens/Messagescreen.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useLayoutEffect } from "react"; 2 | import { 3 | FlatList, 4 | Keyboard, 5 | Pressable, 6 | StyleSheet, 7 | Text, 8 | TextInput, 9 | View, 10 | } from "react-native"; 11 | import { GlobalContext } from "../context"; 12 | import Messagecomponent from "../components/Messagecomponent"; 13 | import {socket} from "../utils/index"; 14 | 15 | export default function Messagescreen({ navigation, route }) { 16 | const { currentGroupName, currentGroupID } = route.params; 17 | const { 18 | allChatMessages, 19 | setAllChatMessages, 20 | currentUser, 21 | currentChatMesage, 22 | setCurrentChatMessage, 23 | } = useContext(GlobalContext); 24 | 25 | function handleAddNewMessage() { 26 | const timeData = { 27 | hr: 28 | new Date().getHours() < 10 29 | ? `0${new Date().getHours()}` 30 | : new Date().getHours(), 31 | mins: 32 | new Date().getMinutes() < 10 33 | ? `0${new Date().getMinutes()}` 34 | : new Date().getMinutes(), 35 | }; 36 | 37 | if (currentUser) { 38 | socket.emit("newChatMessage", { 39 | currentChatMesage, 40 | groupIdentifier: currentGroupID, 41 | currentUser, 42 | timeData, 43 | }); 44 | 45 | setCurrentChatMessage(""); 46 | Keyboard.dismiss(); 47 | } 48 | } 49 | 50 | 51 | useEffect(()=>{ 52 | socket.emit('findGroup', currentGroupID) 53 | socket.on('foundGroup', (allChats)=> setAllChatMessages(allChats)) 54 | },[socket]) 55 | 56 | 57 | return ( 58 | 59 | 62 | {allChatMessages && allChatMessages[0] ? ( 63 | ( 66 | 67 | )} 68 | keyExtractor={(item) => item.id} 69 | /> 70 | ) : ( 71 | "" 72 | )} 73 | 74 | 75 | setCurrentChatMessage(value)} 79 | placeholder="Enter your message" 80 | /> 81 | 82 | 83 | 84 | SEND 85 | 86 | 87 | 88 | 89 | ); 90 | } 91 | 92 | const styles = StyleSheet.create({ 93 | wrapper: { 94 | flex: 1, 95 | backgroundColor: "#eee", 96 | }, 97 | messageInputContainer: { 98 | width: "100%", 99 | backgroundColor: "#fff", 100 | paddingVertical: 30, 101 | paddingHorizontal: 15, 102 | justifyContent: "center", 103 | flexDirection: "row", 104 | }, 105 | messageInput: { 106 | borderWidth: 1, 107 | padding: 15, 108 | flex: 1, 109 | borderRadius: 50, 110 | marginRight: 10, 111 | }, 112 | button: { 113 | width: "30%", 114 | backgroundColor: "#703efe", 115 | alignItems: "center", 116 | justifyContent: "center", 117 | borderRadius: 50, 118 | }, 119 | buttonText: { 120 | color: "#fff", 121 | fontSize: 20, 122 | }, 123 | }); 124 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | import { Platform } from "react-native"; 2 | import { io } from "socket.io-client"; 3 | export const BaseUrl = 4 | Platform.OS === "android" ? "http://10.0.2.2:3000/" : "http://localhost:3000"; 5 | 6 | export const socket = io.connect("http://10.0.2.2:4000/"); 7 | --------------------------------------------------------------------------------