├── .watchmanconfig ├── .gitattributes ├── app.json ├── babel.config.js ├── .env ├── server ├── .env ├── package.json └── index.js ├── .buckconfig ├── index.js ├── .editorconfig ├── metro.config.js ├── App.js ├── Root.js ├── .gitignore ├── src ├── components │ ├── ChatBubble.js │ ├── AudioPlayer.js │ └── VideoPlayer.js └── screens │ ├── Login.js │ ├── Groups.js │ └── Chat.js ├── package.json ├── .flowconfig └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNChatkitDemo", 3 | "displayName": "RNChatkitDemo" 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID" 2 | CHATKIT_SECRET_KEY="YOUR CHATKIT SECRET KEY" -------------------------------------------------------------------------------- /server/.env: -------------------------------------------------------------------------------- 1 | CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID" 2 | CHATKIT_SECRET_KEY="YOUR CHATKIT SECRET KEY" -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | quote_type = double 9 | 10 | 11 | [*.js] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.mustache] 16 | indent_style = space 17 | indent_size = 2 -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View } from "react-native"; 3 | 4 | import Root from "./Root"; 5 | 6 | export default class App extends Component { 7 | 8 | render() { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | const styles = { 18 | container: { 19 | flex: 1 20 | } 21 | }; -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatkit-demo-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@pusher/chatkit-server": "^1.1.0", 14 | "body-parser": "^1.18.3", 15 | "cors": "^2.8.5", 16 | "dotenv": "^7.0.0", 17 | "express": "^4.16.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Root.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { YellowBox } from 'react-native'; 3 | import { createStackNavigator, createAppContainer } from 'react-navigation'; 4 | import Login from './src/screens/Login'; 5 | import Groups from './src/screens/Groups'; 6 | import Chat from './src/screens/Chat'; 7 | 8 | YellowBox.ignoreWarnings(["Setting a timer"]); 9 | 10 | const RootStack = createStackNavigator( 11 | { 12 | Login, 13 | Groups, 14 | Chat 15 | }, 16 | { 17 | initialRouteName: "Login" 18 | } 19 | ); 20 | 21 | const AppContainer = createAppContainer(RootStack); 22 | 23 | class Router extends Component { 24 | render() { 25 | return ; 26 | } 27 | } 28 | 29 | export default Router; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /src/components/ChatBubble.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View } from "react-native"; 3 | import { MessageText, Time } from "react-native-gifted-chat"; 4 | 5 | const ChatBubble = (props) => { 6 | const { position, children, currentMessage, uri } = props; 7 | return ( 8 | 9 | 10 | 11 | {children} 12 | 14 | 15 | ); 16 | } 17 | 18 | const styles = { 19 | left: { 20 | container: { 21 | flex: 1, 22 | alignItems: 'flex-start', 23 | }, 24 | wrapper: { 25 | borderRadius: 15, 26 | backgroundColor: '#f0f0f0', 27 | marginRight: 60, 28 | minHeight: 20, 29 | justifyContent: 'flex-end', 30 | } 31 | }, 32 | right: { 33 | container: { 34 | flex: 1, 35 | alignItems: 'flex-end', 36 | }, 37 | wrapper: { 38 | borderRadius: 15, 39 | backgroundColor: '#0084ff', 40 | marginLeft: 60, 41 | minHeight: 20, 42 | justifyContent: 'flex-end', 43 | } 44 | } 45 | } 46 | 47 | export default ChatBubble; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNChatkitDemo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "@pusher/chatkit-client": "^1.4.1", 11 | "axios": "^0.18.0", 12 | "react": "16.8.3", 13 | "react-native": "0.59.1", 14 | "react-native-audio-toolkit": "^1.0.6", 15 | "react-native-config": "^0.11.7", 16 | "react-native-document-picker": "^2.3.0", 17 | "react-native-fs": "^2.13.3", 18 | "react-native-gesture-handler": "^1.1.0", 19 | "react-native-gifted-chat": "^0.7.2", 20 | "react-native-mime-types": "^2.2.1", 21 | "react-native-modal": "^9.0.0", 22 | "react-native-vector-icons": "^6.4.2", 23 | "react-native-video": "^4.4.0", 24 | "react-navigation": "^3.5.1", 25 | "rn-fetch-blob": "^0.10.15" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.4.0", 29 | "@babel/runtime": "^7.4.0", 30 | "babel-jest": "^24.5.0", 31 | "jest": "^24.5.0", 32 | "metro-react-native-babel-preset": "^0.53.1", 33 | "react-test-renderer": "16.8.3" 34 | }, 35 | "jest": { 36 | "preset": "react-native" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | 28 | [options] 29 | emoji=true 30 | 31 | esproposal.optional_chaining=enable 32 | esproposal.nullish_coalescing=enable 33 | 34 | module.system=haste 35 | module.system.haste.use_name_reducers=true 36 | # get basename 37 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 38 | # strip .js or .js.flow suffix 39 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 40 | # strip .ios suffix 41 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 42 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 43 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 44 | module.system.haste.paths.blacklist=.*/__tests__/.* 45 | module.system.haste.paths.blacklist=.*/__mocks__/.* 46 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 47 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 48 | 49 | munge_underscores=true 50 | 51 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 52 | 53 | module.file_ext=.js 54 | module.file_ext=.jsx 55 | module.file_ext=.json 56 | module.file_ext=.native.js 57 | 58 | suppress_type=$FlowIssue 59 | suppress_type=$FlowFixMe 60 | suppress_type=$FlowFixMeProps 61 | suppress_type=$FlowFixMeState 62 | 63 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 65 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 66 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 67 | 68 | [version] 69 | ^0.92.0 70 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require("body-parser"); 3 | const cors = require("cors"); 4 | const Chatkit = require("@pusher/chatkit-server"); 5 | 6 | require("dotenv").config(); 7 | 8 | const app = express(); 9 | const INSTANCE_LOCATOR_ID = process.env.CHATKIT_INSTANCE_LOCATOR_ID; 10 | const CHATKIT_SECRET = process.env.CHATKIT_SECRET_KEY; 11 | 12 | const chatkit = new Chatkit.default({ 13 | instanceLocator: `v1:us1:${INSTANCE_LOCATOR_ID}`, 14 | key: CHATKIT_SECRET 15 | }); 16 | 17 | app.use(bodyParser.urlencoded({ extended: false })); 18 | app.use(bodyParser.json()); 19 | app.use(cors()); 20 | 21 | app.post("/auth", (req, res) => { 22 | const { user_id } = req.query; 23 | const authData = chatkit.authenticate({ 24 | userId: user_id 25 | }); 26 | 27 | res.status(authData.status) 28 | .send(authData.body); 29 | }); 30 | 31 | let users = []; 32 | app.get("/users", async (req, res) => { 33 | try { 34 | users = await chatkit.getUsers(); 35 | res.send({ users }); 36 | } catch (get_users_err) { 37 | console.log("error getting users: ", get_users_err); 38 | } 39 | }); 40 | 41 | app.post("/user", async (req, res) => { 42 | // note: don't forget to access http://localhost:5000/users on your browser first 43 | // because this route depends on the users variable to be filled 44 | const { username } = req.body; 45 | try { 46 | const user = users.find((usr) => usr.name == username); 47 | res.send({ user }); 48 | } catch (get_user_err) { 49 | console.log("error getting user: ", get_user_err); 50 | } 51 | }); 52 | 53 | 54 | app.post("/user/permissions", async(req, res) => { 55 | const { room_id, user_id } = req.body; 56 | try { 57 | const roles = await chatkit.getUserRoles({ userId: user_id }); 58 | const role = roles.find(role => role.room_id == room_id); 59 | const permissions = (role) ? role.permissions : []; 60 | 61 | res.send({ permissions }); 62 | } catch (user_permissions_err) { 63 | console.log("error getting user permissions: ", user_permissions_err); 64 | } 65 | }); 66 | 67 | 68 | app.post("/rooms", async (req, res) => { 69 | const { user_id } = req.body; 70 | try { 71 | const rooms = await chatkit.getUserRooms({ 72 | userId: user_id 73 | }); 74 | res.send({ rooms }); 75 | } catch (get_rooms_err) { 76 | console.log("error getting rooms: ", get_rooms_err); 77 | } 78 | }); 79 | 80 | const PORT = 5000; 81 | app.listen(PORT, (err) => { 82 | if (err) { 83 | console.error(err); 84 | } else { 85 | console.log(`Running on ports ${PORT}`); 86 | } 87 | }); -------------------------------------------------------------------------------- /src/screens/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View, Text, TextInput, Button } from "react-native"; 3 | import axios from "axios"; 4 | 5 | const CHAT_SERVER = "YOUR NGROK HTTPS URL"; 6 | 7 | class Login extends Component { 8 | static navigationOptions = { 9 | title: "Login" 10 | }; 11 | 12 | 13 | state = { 14 | username: "", 15 | is_loading: false 16 | }; 17 | 18 | // 19 | 20 | render() { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | Enter your username 28 | this.setState({ username })} 31 | value={this.state.username} 32 | /> 33 | 34 | 35 | {!this.state.is_loading && ( 36 |