├── .gitignore ├── .idea └── .gitignore ├── App.tsx ├── README.md ├── app.json ├── assets ├── fonts │ └── SpaceMono-Regular.ttf └── images │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── chat.png ├── data └── FirebaseStorage.ts ├── hooks ├── useCachedResources.ts └── useColorScheme.ts ├── navigation └── index.tsx ├── package.json ├── screens ├── ChatScreen.tsx └── HomeScreen.tsx ├── tsconfig.json ├── types.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Expo 2 | .expo 3 | __generated__ 4 | web-build 5 | 6 | # macOS 7 | .DS_Store 8 | 9 | # Node 10 | node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # Ruby 15 | .direnv 16 | 17 | # Emacs 18 | *~ 19 | 20 | # Vim 21 | .*.swp 22 | .*.swo 23 | .*.swn 24 | .*.swm 25 | 26 | # Sublime Text 27 | *.sublime-project 28 | *.sublime-workspace 29 | 30 | # Xcode 31 | *.pbxuser 32 | !default.pbxuser 33 | *.xccheckout 34 | *.xcscmblueprint 35 | xcuserdata 36 | 37 | # Android Studio 38 | *.iml 39 | .gradle 40 | .idea/libraries 41 | .idea/workspace.xml 42 | .idea/gradle.xml 43 | .idea/misc.xml 44 | .idea/modules.xml 45 | .idea/vcs.xml 46 | 47 | # Eclipse 48 | .project 49 | .settings 50 | 51 | # VSCode 52 | .history/ 53 | 54 | # Android 55 | *.apk 56 | *.hprof 57 | /android/**/build 58 | /android/**/local.properties 59 | /android/captures 60 | /android/expoview/libs/ReactAndroid-temp 61 | /android/versioned-react-native/ReactAndroid 62 | ReactAndroid-temp.aar 63 | 64 | # Tools 65 | jarjar-rules.txt 66 | 67 | # Dynamic Macros 68 | /ios/Exponent/Supporting/EXBuildConstants.plist.bak 69 | /ios/Exponent/Supporting/EXBuildConstants.plist.json 70 | /ios/Exponent/Supporting/EXBuildConstants.plist 71 | /ios/Exponent/Supporting/GoogleService-Info.plist 72 | /ios/Exponent/Supporting/Info.plist.bak 73 | /ios/Exponent/Supporting/Info.plist.json 74 | /android/expoview/src/main/java/host/exp/exponent/generated/ExponentBuildConstants.java 75 | /android/app/src/androidTest/java/host/exp/exponent/generated/TestBuildConstants.java 76 | .kernel-ngrok-url 77 | 78 | # Template files 79 | /android/app/src/main/AndroidManifest.xml 80 | /android/app/google-services.json 81 | /android/expoview/src/main/java/host/exp/exponent/generated/ExponentKeys.java 82 | /apps/bare-expo/android/app/google-services.json 83 | /apps/bare-expo/ios/BareExpo/GoogleService-Info.plist 84 | /ios/Exponent/Generated/EXKeys.h 85 | /ios/ExponentIntegrationTests/EXTestEnvironment.plist 86 | /exponent-view-template/ios/Podfile 87 | 88 | # Template projects 89 | templates/**/android/**/generated/* 90 | templates/**/android/app/build 91 | templates/**/Pods/** 92 | 93 | # Codemod 94 | .codemod.bookmark 95 | 96 | # Fastlane 97 | /*.cer 98 | /fastlane/report.xml 99 | /fastlane/Preview.html 100 | /fastlane/Deployment 101 | /Preview.html 102 | /gc_keys.json 103 | 104 | # CI 105 | /android/logcat.txt 106 | 107 | # Shell apps 108 | android-shell-app 109 | shellAppBase-* 110 | shellAppIntermediates 111 | shellAppWorkspaces 112 | /artifacts/* 113 | 114 | # Expo Client builds 115 | /client-builds 116 | 117 | # Expo web env 118 | .env.local 119 | .env.development.local 120 | .env.test.local 121 | .env.production.local 122 | apps/bare-expo/deploy-url.txt 123 | 124 | # Expo Doc merging 125 | docs/pages/versions/*/react-native/ADDED_*.md 126 | docs/pages/versions/*/react-native/REMOVED_*.md 127 | docs/pages/versions/*/react-native/*.diff 128 | 129 | # Home 130 | /home/dist 131 | 132 | # Prebuilds 133 | /packages/**/*.xcframework 134 | /packages/**/*.spec.json 135 | /packages/**/Info-generated.plist 136 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /App.tsx: -------------------------------------------------------------------------------- 1 | import {StatusBar} from 'expo-status-bar'; 2 | import {SafeAreaProvider} from 'react-native-safe-area-context'; 3 | 4 | import useCachedResources from './hooks/useCachedResources'; 5 | import useColorScheme from './hooks/useColorScheme'; 6 | import Navigation from './navigation'; 7 | 8 | export default function App() { 9 | const isLoadingComplete = useCachedResources(); 10 | const colorScheme = useColorScheme(); 11 | 12 | if (!isLoadingComplete) { 13 | return null; 14 | } else { 15 | return ( 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Supports Expo iOS 4 | 5 | Supports Expo Android 6 |

7 | 8 | # React Native Firebase Chat 😎 9 | 10 | This example demonstrates how you can build chat using Firebase. 11 | 12 | ![Chat screenshot](https://github.com/avegrv/react-native-firebase-chat/raw/master/chat.png) 13 | 14 | ## 🚀 How to use 15 | 16 | - Run `yarn` or `npm install` 17 | - Run [`expo start`]. If you don't have expo cli, use `yarn global add expo-cli` 18 | - Please chat 19 | 20 | ## 🔌 Libraries in use 21 | 22 | - firebase 23 | - react-native-gifted-chat 24 | - react-navigation 25 | 26 | ## 📝 Inspired by 27 | - [Expo: How To Build A Chat App With React Native](https://blog.expo.io/how-to-build-a-chat-app-with-react-native-3ef8604ebb3c) 28 | - [Phylypo Tum: React Native Simple Chat with Firebase and GiftedChat](https://medium.com/@phylypo/react-native-simple-chat-with-firebase-and-giftedchat-f7dbdff2883a) 29 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "react-native-chat", 4 | "slug": "snack-2e1e932e-b897-4cb2-9dda-6ec425feacb4", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "updates": { 16 | "fallbackToCacheTimeout": 0 17 | }, 18 | "assetBundlePatterns": [ 19 | "**/*" 20 | ], 21 | "ios": { 22 | "supportsTablet": true 23 | }, 24 | "android": { 25 | "adaptiveIcon": { 26 | "foregroundImage": "./assets/images/adaptive-icon.png", 27 | "backgroundColor": "#ffffff" 28 | } 29 | }, 30 | "web": { 31 | "favicon": "./assets/images/favicon.png" 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/assets/images/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avegrv/react-native-firebase-chat/f75a1c566b66ba004e50e8fa0b197c1cb106710a/chat.png -------------------------------------------------------------------------------- /data/FirebaseStorage.ts: -------------------------------------------------------------------------------- 1 | import {getApps} from 'firebase/app'; 2 | import firebase from "firebase/compat"; 3 | 4 | class FirebaseStorage { 5 | constructor() { 6 | this.init(); 7 | } 8 | 9 | get uid() { 10 | return (firebase.auth().currentUser || {}).uid; 11 | } 12 | 13 | get ref() { 14 | return firebase.database().ref('messages'); 15 | } 16 | 17 | get timestamp() { 18 | return firebase.database.ServerValue.TIMESTAMP; 19 | } 20 | 21 | init = () => { 22 | if (!getApps().length) { 23 | const firebaseConfig = { 24 | apiKey: "AIzaSyDyMmFzMv1HE7pJ-EyO6eHLDmuuKrCacU0", 25 | authDomain: "fir-chat-4e842.firebaseapp.com", 26 | databaseURL: "https://fir-chat-4e842-default-rtdb.firebaseio.com", 27 | projectId: "fir-chat-4e842", 28 | storageBucket: "fir-chat-4e842.appspot.com", 29 | messagingSenderId: "295778429451", 30 | appId: "1:295778429451:web:22c386c808e3b5a3c2750e", 31 | measurementId: "G-HT05XS6NXL" 32 | }; 33 | firebase.initializeApp(firebaseConfig); 34 | firebase.auth().onAuthStateChanged(this.onAuthStateChanged); 35 | } 36 | } 37 | 38 | onAuthStateChanged = (user: firebase.User | null) => { 39 | if (!user) { 40 | try { 41 | firebase.auth().signInAnonymously(); 42 | } catch ({message}) { 43 | alert(message); 44 | } 45 | } 46 | }; 47 | 48 | parse = (snapshot: any) => { 49 | const {timestamp: numberStamp, text, user} = snapshot.val(); 50 | const {key: _id} = snapshot; 51 | const timestamp = new Date(numberStamp); 52 | return { 53 | _id, 54 | timestamp, 55 | text, 56 | user, 57 | }; 58 | }; 59 | 60 | on = (callback: (messages: any) => void) => 61 | this.ref 62 | .limitToLast(20) 63 | .on('child_added', snapshot => callback(this.parse(snapshot))); 64 | 65 | send = (messages: any[]) => { 66 | for (let i = 0; i < messages.length; i++) { 67 | const {text, user} = messages[i]; 68 | const message = { 69 | text, 70 | user, 71 | timestamp: this.timestamp, 72 | }; 73 | this.append(message); 74 | } 75 | }; 76 | 77 | append = (message: any) => this.ref.push(message); 78 | 79 | off() { 80 | this.ref.off(); 81 | } 82 | } 83 | 84 | const instance = new FirebaseStorage() 85 | 86 | export default instance; 87 | -------------------------------------------------------------------------------- /hooks/useCachedResources.ts: -------------------------------------------------------------------------------- 1 | import { FontAwesome } from '@expo/vector-icons'; 2 | import * as Font from 'expo-font'; 3 | import * as SplashScreen from 'expo-splash-screen'; 4 | import { useEffect, useState } from 'react'; 5 | 6 | export default function useCachedResources() { 7 | const [isLoadingComplete, setLoadingComplete] = useState(false); 8 | 9 | // Load any resources or data that we need prior to rendering the app 10 | useEffect(() => { 11 | async function loadResourcesAndDataAsync() { 12 | try { 13 | SplashScreen.preventAutoHideAsync(); 14 | 15 | // Load fonts 16 | await Font.loadAsync({ 17 | ...FontAwesome.font, 18 | 'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'), 19 | }); 20 | } catch (e) { 21 | // We might want to provide this error information to an error reporting service 22 | console.warn(e); 23 | } finally { 24 | setLoadingComplete(true); 25 | SplashScreen.hideAsync(); 26 | } 27 | } 28 | 29 | loadResourcesAndDataAsync(); 30 | }, []); 31 | 32 | return isLoadingComplete; 33 | } 34 | -------------------------------------------------------------------------------- /hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | import { ColorSchemeName, useColorScheme as _useColorScheme } from 'react-native'; 2 | 3 | // The useColorScheme value is always either light or dark, but the built-in 4 | // type suggests that it can be null. This will not happen in practice, so this 5 | // makes it a bit easier to work with. 6 | export default function useColorScheme(): NonNullable { 7 | return _useColorScheme() as NonNullable; 8 | } 9 | -------------------------------------------------------------------------------- /navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import {DarkTheme, DefaultTheme, NavigationContainer} from '@react-navigation/native'; 2 | import {createNativeStackNavigator} from '@react-navigation/native-stack'; 3 | import * as React from 'react'; 4 | import {ColorSchemeName} from 'react-native'; 5 | import HomeScreen from "../screens/HomeScreen"; 6 | import {RootStackParamList} from "../types"; 7 | import ChatScreen from "../screens/ChatScreen"; 8 | 9 | export default function Navigation({colorScheme}: { colorScheme: ColorSchemeName }) { 10 | return ( 11 | 13 | 14 | 15 | ); 16 | } 17 | 18 | const RootStack = createNativeStackNavigator(); 19 | 20 | function RootNavigator() { 21 | return ( 22 | 23 | 24 | 25 | 26 | ); 27 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject", 9 | "test": "jest --watchAll" 10 | }, 11 | "dependencies": { 12 | "firebase": "9.6.4", 13 | "moment": "*", 14 | "prop-types": "*", 15 | "react-native-gesture-handler": "~1.8.0", 16 | "react-native-gifted-chat": "0.16.3", 17 | "@expo/vector-icons": "^12.0.0", 18 | "@react-navigation/bottom-tabs": "^6.0.5", 19 | "@react-navigation/native": "^6.0.2", 20 | "@react-navigation/native-stack": "^6.1.0", 21 | "expo": "~44.0.0", 22 | "expo-asset": "~8.4.4", 23 | "expo-constants": "~13.0.0", 24 | "expo-font": "~10.0.4", 25 | "expo-linking": "~3.0.0", 26 | "expo-splash-screen": "~0.14.0", 27 | "expo-status-bar": "~1.2.0", 28 | "expo-web-browser": "~10.1.0", 29 | "react": "17.0.1", 30 | "react-dom": "17.0.1", 31 | "react-native": "0.64.3", 32 | "react-native-safe-area-context": "3.3.2", 33 | "react-native-screens": "~3.10.1", 34 | "react-native-web": "0.17.5" 35 | }, 36 | "devDependencies": { 37 | "@babel/core": "^7.12.9", 38 | "@types/react": "~17.0.21", 39 | "@types/react-native": "~0.64.12", 40 | "jest-expo": "~44.0.1", 41 | "jest": "^26.6.3", 42 | "react-test-renderer": "17.0.1", 43 | "typescript": "~4.3.5" 44 | }, 45 | "private": true 46 | } 47 | -------------------------------------------------------------------------------- /screens/ChatScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useRef, useState} from "react"; 2 | import FirebaseStorage from "../data/FirebaseStorage"; 3 | import {SafeAreaView} from "react-native"; 4 | import {GiftedChat, IMessage, User} from "react-native-gifted-chat"; 5 | import {ChatProps} from "../types"; 6 | 7 | const ChatScreen: React.FC = (props) => { 8 | const [messages, setMessages] = useState([]) 9 | const prevMessages = useRef([]); 10 | const user = { 11 | name: props.route.params.name, 12 | _id: FirebaseStorage.uid, 13 | } as User; 14 | useEffect(() => { 15 | FirebaseStorage.on(messages => { 16 | const newMessages = GiftedChat.append(prevMessages.current, messages) 17 | prevMessages.current = newMessages 18 | setMessages(newMessages) 19 | }) 20 | return function cleanup() { 21 | FirebaseStorage.off(); 22 | } 23 | }, []) 24 | return 25 | 30 | 31 | } 32 | 33 | export default ChatScreen -------------------------------------------------------------------------------- /screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {StyleSheet, Text, TextInput, TouchableOpacity, View,} from 'react-native'; 3 | import {HomeProps} from "../types"; 4 | 5 | const HomeScreen: React.FC = ({navigation}) => { 6 | const [name, setName] = useState('') 7 | return ( 8 | 9 | Enter your name: 10 | 16 | 17 | Next 18 | 19 | 20 | ); 21 | 22 | function onNextPress() { 23 | navigation.navigate('Chat', {name}); 24 | } 25 | } 26 | 27 | const offset = 24; 28 | const styles = StyleSheet.create({ 29 | title: { 30 | marginTop: offset, 31 | marginLeft: offset, 32 | fontSize: offset, 33 | }, 34 | nameInput: { 35 | height: offset * 2, 36 | 37 | margin: offset, 38 | paddingHorizontal: offset, 39 | borderColor: '#111111', 40 | borderWidth: 1, 41 | }, 42 | buttonText: { 43 | marginLeft: offset, 44 | fontSize: offset, 45 | }, 46 | }); 47 | 48 | export default HomeScreen; 49 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | import {NativeStackScreenProps} from "@react-navigation/native-stack"; 2 | 3 | declare global { 4 | namespace ReactNavigation { 5 | interface RootParamList extends RootStackParamList { 6 | } 7 | } 8 | } 9 | 10 | export type RootStackParamList = { 11 | Home: undefined; 12 | Chat: { name: string }; 13 | }; 14 | 15 | export type HomeProps = NativeStackScreenProps; 16 | export type ChatProps = NativeStackScreenProps; --------------------------------------------------------------------------------