├── .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 |
4 |
5 |
6 |
7 |
8 | # React Native Firebase Chat 😎
9 |
10 | This example demonstrates how you can build chat using Firebase.
11 |
12 | 
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;
--------------------------------------------------------------------------------