├── Example ├── .watchmanconfig ├── capture.png ├── android │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ └── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── reactnativetwilioipmessagingexample │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ └── gradlew.bat ├── ios │ ├── ReactNativeTwilioIPMessagingExample.xcworkspace │ │ └── xcuserdata │ │ │ └── bbumbalough.xcuserdatad │ │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ ├── GiftedMessengerExample.xcworkspace │ │ └── contents.xcworkspacedata │ ├── GiftedMessengerExample │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Images.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── AppDelegate.m │ │ └── Base.lproj │ │ │ └── LaunchScreen.xib │ ├── ReactNativeTwilioIPMessagingExample.xcodeproj │ │ ├── xcuserdata │ │ │ └── bbumbalough.xcuserdatad │ │ │ │ └── xcschemes │ │ │ │ ├── xcschememanagement.plist │ │ │ │ └── ReactNativeTwilioIPMessagingExample.xcscheme │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── GiftedMessengerExample.xcscheme │ └── Podfile ├── index.android.js ├── index.ios.js ├── package.json ├── README.md ├── Navigation.js ├── .flowconfig └── GiftedMessengerContainer.js ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── bradbumbalough │ │ └── RCTTwilioChat │ │ ├── RCTTwilioChatPackage.java │ │ ├── RCTTwilioAccessManager.java │ │ ├── RCTTwilioChatPaginator.java │ │ └── RCTTwilioChatMembers.java └── build.gradle ├── ios ├── RCTTwilioChat.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── RCTTwilioChat │ ├── RCTTwilioChatMembers.h │ ├── RCTTwilioChatMessages.h │ ├── RCTTwilioChatClient.h │ ├── RCTTwilioChatChannels.h │ ├── RCTTwilioAccessManager.h │ ├── RCTTwilioChatPaginator.h │ ├── RCTConvert+TwilioChatClient.h │ ├── RCTTwilioAccessManager.m │ ├── RCTTwilioChatPaginator.m │ ├── RCTTwilioChatMembers.m │ ├── RCTConvert+TwilioChatClient.m │ ├── RCTTwilioChatMessages.m │ └── RCTTwilioChatChannels.m ├── Podfile ├── Podfile.lock └── RCTTwilioChat.podspec ├── docs ├── README.md ├── Member.md ├── Paginator.md ├── Message.md ├── UserInfo.md ├── Constants.md ├── AccessManager.md ├── Channel.md └── Client.md ├── lib ├── index.js ├── Member.js ├── Constants.js ├── Message.js ├── UserInfo.js ├── Paginator.js ├── AccessManager.js ├── Channel.js └── Client.js ├── .eslintrc ├── .gitignore ├── package.json ├── LICENSE ├── CONTRIBUTING.md ├── MIGRATION.md └── README.md /Example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /Example/capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/HEAD/Example/capture.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactNativeTwilioIPMessagingExample 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/HEAD/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/HEAD/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/HEAD/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/HEAD/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/RCTTwilioChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ios/ReactNativeTwilioIPMessagingExample.xcworkspace/xcuserdata/bbumbalough.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/index.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ReactNative = require('react-native'); 4 | var { 5 | AppRegistry 6 | } = ReactNative; 7 | 8 | 9 | AppRegistry.registerComponent('ReactNativeTwilioIPMessagingExample', () => require('./Navigation')); 10 | -------------------------------------------------------------------------------- /Example/index.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ReactNative = require('react-native'); 4 | var { 5 | AppRegistry 6 | } = ReactNative; 7 | 8 | 9 | AppRegistry.registerComponent('ReactNativeTwilioIPMessagingExample', () => require('./Navigation')); 10 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Classes 4 | - [AccessManager](AccessManager.md) 5 | - [Client](Client.md) 6 | - [Channel](Channel.md) 7 | - [Message](Message.md) 8 | - [Member](Member.md) 9 | - [UserInfo](UserInfo.md) 10 | - [Paginator](Paginator.md) 11 | - [Constants](Constants.md) 12 | -------------------------------------------------------------------------------- /docs/Member.md: -------------------------------------------------------------------------------- 1 | # Member 2 | 3 | ## Properties 4 | |Name |Type |Description | 5 | |--- |--- |--- | 6 | |*userInfo*|UserInfo|The user info object of the member 7 | |*lastConsumedMessageIndex*|Integer|The index of the last message the member consumed 8 | |*lastConsumptionTimestamp*|Date|The timestamp of the last message consumption -------------------------------------------------------------------------------- /docs/Paginator.md: -------------------------------------------------------------------------------- 1 | # Paginator 2 | 3 | ## Properties 4 | |Name |Type |Description | 5 | |--- |--- |--- | 6 | |*hasNextPage*|Boolean|Whether there is another page to request 7 | |*items*|Array / Array|The items returned for this page as class instances 8 | 9 | ## Methods 10 | 11 | ### `nextPage()` : Promise 12 | Returns the next page of the items.s -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | exports.AccessManager = require('./AccessManager').default; 2 | exports.Client = require('./Client').default; 3 | exports.Channel = require('./Channel').default; 4 | exports.UserInfo = require('./UserInfo').default; 5 | exports.Message = require('./Message').default; 6 | exports.Constants = require('./Constants').default; 7 | exports.Paginator = require('./Paginator').default; 8 | -------------------------------------------------------------------------------- /lib/Member.js: -------------------------------------------------------------------------------- 1 | import UserInfo from './UserInfo'; 2 | 3 | class Member { 4 | constructor(props) { 5 | this.userInfo = new UserInfo(props.userInfo); 6 | this.lastConsumedMessageIndex = props.lastConsumedMessageIndex; 7 | this.lastConsumptionTimestamp = this.lastConsumptionTimestamp ? new Date(this.lastConsumptionTimestamp) : null; 8 | } 9 | } 10 | 11 | export default Member; 12 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatMembers.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatMembers.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/7/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface RCTTwilioChatMembers : NSObject 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatMessages.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatMessages.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/3/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface RCTTwilioChatMessages : NSObject 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'RCTTwilioChat' do 5 | # Uncomment this line if you're using Swift or would like to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for RCTTwilioChat 9 | source 'https://github.com/twilio/cocoapod-specs' 10 | pod 'TwilioChatClient', '~> 0.17.1' 11 | pod 'TwilioAccessManager', '~> 0.1.3' 12 | end 13 | -------------------------------------------------------------------------------- /Example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ReactNativeTwilioIPMessagingExample' 2 | 3 | include ':app' 4 | include ':ExtraDimensions', ':app' 5 | project(':ExtraDimensions').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-extra-dimensions-android/android') 6 | include ':RCTTwilioChat', ':app' 7 | project(':RCTTwilioChat').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-chat/android') 8 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TwilioAccessManager (0.1.1) 3 | - TwilioChatClient (0.16.0) 4 | 5 | DEPENDENCIES: 6 | - TwilioAccessManager (~> 0.1.1) 7 | - TwilioChatClient (~> 0.16.0) 8 | 9 | SPEC CHECKSUMS: 10 | TwilioAccessManager: d5513425a63f469aeeb48267f9fa3c83b91f75ef 11 | TwilioChatClient: 0241ea050d042395493dea8920d1fa1ac45ca478 12 | 13 | PODFILE CHECKSUM: 7ffd24c196e7a221b280de18ef37837c84912830 14 | 15 | COCOAPODS: 1.0.0 16 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "allowImportExportEverywhere": true 5 | }, 6 | "extends": [ 7 | "airbnb" 8 | ], 9 | "settings": { 10 | "import/core-modules": ["react-native"], 11 | "no-unused-vars": ["error", { "varsIgnorePattern": "React" }] 12 | }, 13 | "rules": { 14 | "import/extensions": ["off", "never"], 15 | "no-underscore-dangle": 0 16 | } 17 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 19 9 | targetSdkVersion 23 10 | versionCode 11 11 | versionName "0.1.1" 12 | } 13 | } 14 | dependencies { 15 | compile 'com.facebook.react:react-native:+' 16 | compile "com.twilio:chat-android:0.12.1" 17 | compile "com.twilio:accessmanager-android:0.1.0" 18 | } 19 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatClient.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 5/31/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RCTTwilioChatClient : NSObject { 13 | TwilioChatClient *client; 14 | } 15 | 16 | @property (nonatomic, retain) TwilioChatClient *client; 17 | 18 | + (id)sharedManager; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatChannels.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTCHChannels.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/2/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface RCTTwilioChatChannels : NSObject 14 | 15 | + (void)loadChannelFromSid:(NSString *)sid :(void (^)(TCHResult *result, TCHChannel *channel))completion; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/reactnativetwilioipmessagingexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnativetwilioipmessagingexample; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "ReactNativeTwilioIPMessagingExample"; 14 | } 15 | } -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioAccessManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioAccessManager.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 5/31/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | #import 9 | #import 10 | 11 | @interface RCTTwilioAccessManager : NSObject { 12 | TwilioAccessManager *accessManager; 13 | } 14 | 15 | @property (nonatomic, strong) TwilioAccessManager *accessManager; 16 | 17 | + (instancetype)sharedManager; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactNativeTwilioIPMessagingExample", 3 | "version": "0.2.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start" 7 | }, 8 | "dependencies": { 9 | "@exponent/react-native-navigator": "^0.4.2", 10 | "react": "^15.4.1", 11 | "react-native": "^0.39.0", 12 | "react-native-communications": "^1.0.1", 13 | "react-native-extra-dimensions-android": "^0.17.0", 14 | "react-native-gifted-messenger": "^0.1.4", 15 | "react-native-twilio-ip-chat": "^0.1.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatPaginator.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatPaginator.h 3 | // RCTTwilioChat 4 | // 5 | // Created by Brad Bumbalough on 12/2/16. 6 | // Copyright © 2016 Brad Bumbalough. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RCTTwilioChatPaginator : NSObject { 13 | NSMutableDictionary *paginators; 14 | } 15 | 16 | @property (nonatomic, retain) NSMutableDictionary *paginators; 17 | 18 | + (id)sharedManager; 19 | + (NSString*)setPaginator:(id)paginator; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ios 2 | Example/ios/Pods 3 | ios/Pods 4 | *.xcuserstate 5 | 6 | # android 7 | *.iml 8 | android/.gradle 9 | android/local.properties 10 | android/.idea 11 | android/.idea 12 | android/build 13 | android/app/build 14 | android/gradle 15 | android/captures 16 | 17 | # android example 18 | Example/android/.gradle 19 | Example/android/local.properties 20 | Example/android/.idea 21 | Example/android/.idea 22 | Example/android/build 23 | Example/android/app/build 24 | Example/android/gradle 25 | Example/android/captures 26 | 27 | Example/node_modules 28 | 29 | .DS_Store 30 | 31 | node_modules 32 | 33 | .vscode 34 | 35 | build 36 | xcuserdata 37 | .idea/ 38 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RCTTwilioChat" 3 | s.version = "0.1.1" 4 | s.summary = "React Native wrapper for Twilio Programable Chat SDKs" 5 | 6 | s.homepage = "https://github.com/ccm-innovation/react-native-twilio-chat" 7 | 8 | s.license = "MIT" 9 | s.authors = { "Brad Bumbalough" => "bradley.bumbalough@gmail.com" } 10 | s.platform = :ios, "8.1" 11 | 12 | s.source = { :git => "https://github.com/ccm-innovation/react-native-twilio-chat.git" } 13 | 14 | s.source_files = "RCTTwilioChat/*.{h,m}" 15 | 16 | s.dependency 'React' 17 | s.dependency 'TwilioChatClient' 18 | s.dependency 'TwilioAccessManager' 19 | end 20 | -------------------------------------------------------------------------------- /Example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.0.0' 9 | // NOTE: Do not place your application dependencies here; they belong 10 | // in the individual module build.gradle files 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | mavenLocal() 17 | jcenter() 18 | maven { 19 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 20 | url "$rootDir/../node_modules/react-native/android" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /lib/Constants.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | Platform, 4 | } from 'react-native'; 5 | 6 | const { 7 | TwilioChatClient, 8 | } = NativeModules; 9 | 10 | function getConstants() { 11 | if (Platform.OS === 'android') { 12 | return { 13 | TCHChannelStatus: TwilioChatClient.TCHChannelStatus, 14 | TCHChannelSynchronizationStatus: TwilioChatClient.TCHChannelSynchronizationStatus, 15 | TCHChannelType: TwilioChatClient.TCHChannelType, 16 | TCHChannelOption: TwilioChatClient.TCHChannelOption, 17 | TCHClientSynchronizationStatus: TwilioChatClient.TCHClientSynchronizationStatus, 18 | TCHClientSynchronizationStrategy: TwilioChatClient.TCHClientSynchronizationStrategy, 19 | TCHLogLevel: TwilioChatClient.TCHLogLevel, 20 | }; 21 | } 22 | return TwilioChatClient.Constants; 23 | } 24 | 25 | export default getConstants(); 26 | -------------------------------------------------------------------------------- /Example/README.md: -------------------------------------------------------------------------------- 1 | # Example App 2 | 3 | Run `npm install` (in this directory) and then `pod install` (for iOS) to bring in the xcode dependencies. 4 | 5 | This is an example app using [React Native Gifted Messenger](https://github.com/FaridSafi/react-native-gifted-messenger). 6 | 7 | ![](https://raw.githubusercontent.com/ccm-innovation/react-native-twilio-chat/master/Example/capture.png) 8 | 9 | You'll need to run a server locally to generate the access_tokens. I used a version of the [Twilio Chat Quickstart](https://www.twilio.com/docs/api/chat/guides/quickstart-js#download), modified to take an identity param in the `/token` route. 10 | 11 | ```JavaScript 12 | app.get('/token', function(request, response) { 13 | var appName = 'TwilioChatDemo'; 14 | var identity = request.query.identity || randomUsername(); 15 | var deviceId = request.query.device; 16 | ... 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/Message.md: -------------------------------------------------------------------------------- 1 | # Message 2 | 3 | ## Properties 4 | |Name |Type |Description | 5 | |--- |--- |--- | 6 | |*sid*|String|The id of the message 7 | |*index*|Number|The index of the message 8 | |*author*|String|The identity of the author 9 | |*body*|String|The message body 10 | |*timestamp*|Date|The date object of the timestamp 11 | |*dateUpdated*|Date|The date object of when the message was last updated **iOS Only** 12 | |*lastUpdatedBy*|String|The identity of the user who last updated the message **iOS Only** 13 | |*attributes*|Object|Any attributes added to the message 14 | 15 | ## Methods 16 | 17 | ### `updateBody(body) : Promise` 18 | |Name |Type |Description | 19 | |--- |--- |--- | 20 | |*body*|String|The new body of the message 21 | 22 | ### `setAttributes(attributes) : Promise` 23 | |Name |Type |Description | 24 | |--- |--- |--- | 25 | |*attributes*|Object|The new attributes to set on the message 26 | -------------------------------------------------------------------------------- /lib/Message.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | } from 'react-native'; 4 | 5 | const { 6 | TwilioChatMessages, 7 | } = NativeModules; 8 | 9 | class Message { 10 | 11 | constructor(props, channelSid) { 12 | this.sid = props.sid; 13 | this.index = props.index; 14 | this.author = props.author; 15 | this.body = props.body; 16 | this.timestamp = new Date(props.timestamp); 17 | this.dateUpdated = props.dateUpdatedAsDate ? new Date(props.dateUpdated) : null; 18 | this.lastUpdatedBy = props.lastUpdatedBy; 19 | this.attributes = props.attributes; 20 | this._channelSid = channelSid; 21 | } 22 | 23 | updateBody(body) { 24 | return TwilioChatMessages.updateBody(this._channelSid, this.index, body); 25 | } 26 | 27 | setAttributes(attributes) { 28 | return TwilioChatMessages.setAttributes(this._channelSid, this.index, attributes); 29 | } 30 | } 31 | 32 | export default Message; -------------------------------------------------------------------------------- /Example/ios/ReactNativeTwilioIPMessagingExample.xcodeproj/xcuserdata/bbumbalough.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GiftedMessengerExample.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | ReactNativeTwilioIPMessagingExample.xcscheme 13 | 14 | orderHint 15 | 5 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 00E356ED1AD99517003FC87E 21 | 22 | primary 23 | 24 | 25 | 13B07F861A680F5B00A75B9A 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /docs/UserInfo.md: -------------------------------------------------------------------------------- 1 | # UserInfo 2 | 3 | ## Properties 4 | |Name |Type |Description | 5 | |--- |--- |--- | 6 | |*identity*|String|The identity of the user 7 | |*friendlyName*|String|The friendly name of the user 8 | |*attributes*|Object|Any custom attributes assigned to the user 9 | |*isOnline*|Boolean|Whether the user is online 10 | |*isNotifiable*|Boolean|Whether the user is able to receive push notifications 11 | 12 | ## Methods 13 | 14 | ### `setAttributes(attributes)` : Promise 15 | |Name |Type |Description | 16 | |--- |--- |--- | 17 | |*attributes*|Object|The new attributes object 18 | 19 | ### `setFriendlyName(name)` : Promise 20 | |Name |Type |Description | 21 | |--- |--- |--- | 22 | |*friendlyName*|String|The new friendlyName 23 | 24 | #### `close()` 25 | Remove the listeners on this instance. Call in `componentWillUnmount()`. 26 | 27 | ### Events 28 | 29 | #### `onUpdated(type)` 30 | |Name |Type |Description | 31 | |--- |--- |--- | 32 | |*type*|Constants.TCHUserInfoUpdate|The type of update **iOS Only** -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-twilio-chat", 3 | "version": "0.3.1", 4 | "description": "A React Native wrapper for the Twilio Chat iOS and Android SDKs", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "lint": "eslint ." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ccm-innovation/react-native-twilio-chat.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "react", 16 | "ios", 17 | "android", 18 | "twilio", 19 | "messaging", 20 | "chat" 21 | ], 22 | "author": "Brad Bumbalough ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/ccm-innovation/react-native-twilio-chat/issues" 26 | }, 27 | "homepage": "https://github.com/ccm-innovation/react-native-twilio-chat#readme", 28 | "devDependencies": { 29 | "babel-eslint": "^7.1.1", 30 | "eslint": "^3.11.1", 31 | "eslint-config-airbnb": "^13.0.0", 32 | "eslint-plugin-import": "^2.2.0", 33 | "eslint-plugin-jsx-a11y": "^2.2.3", 34 | "eslint-plugin-react": "^6.8.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Brad Bumbalough 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Example/Navigation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { 4 | Component, 5 | } from 'react'; 6 | import { 7 | Navigator, 8 | StatusBar, 9 | Platform, 10 | } from 'react-native'; 11 | 12 | import ExNavigator from '@exponent/react-native-navigator'; 13 | 14 | let Router = { 15 | GiftedMessenger() { 16 | return { 17 | getSceneClass() { 18 | if (Platform.OS === 'ios') { 19 | StatusBar.setBarStyle('light-content'); 20 | } 21 | return require('./GiftedMessengerContainer'); 22 | }, 23 | getTitle() { 24 | return 'Gifted Messenger'; 25 | }, 26 | }; 27 | }, 28 | }; 29 | 30 | class Navigation extends Component { 31 | render() { 32 | return ( 33 | 46 | ); 47 | } 48 | } 49 | 50 | module.exports = Navigation; 51 | -------------------------------------------------------------------------------- /Example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # NOTE: The underlying Twilio SDKs require a minimum deployment target of 8.1 3 | platform :ios, '8.1' 4 | 5 | source 'https://github.com/CocoaPods/Specs.git' 6 | source 'https://github.com/twilio/cocoapod-specs' 7 | 8 | target 'ReactNativeTwilioIPMessagingExample' do 9 | # Uncomment this line if you're using Swift or would like to use dynamic frameworks 10 | # use_frameworks! 11 | 12 | # Pods for ReactNativeTwilioIPMessagingExample 13 | inherit! :search_paths 14 | 15 | # Import required Lib from react-native 16 | # https://facebook.github.io/react-native/docs/integration-with-existing-apps.html#podfile 17 | pod 'React', :path => '../node_modules/react-native', :subspecs => [ 18 | 'Core', 19 | 'RCTActionSheet', 20 | 'RCTGeolocation', 21 | 'RCTImage', 22 | 'RCTLinkingIOS', 23 | 'RCTNetwork', 24 | 'RCTText', 25 | 'RCTSettings', 26 | 'RCTVibration', 27 | 'RCTWebSocket' 28 | ] 29 | 30 | pod 'RCTTwilioChat', :path => '../node_modules/react-native-twilio-chat/ios' 31 | source 'https://github.com/twilio/cocoapod-specs' 32 | pod 'TwilioChatClient', '~> 0.16.0' 33 | pod 'TwilioAccessManager', '~> 0.1.1' 34 | end -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We rely on the community to help maintain this project so please submit issues and PRs to help make this a better package. 4 | 5 | ### When submitting issues... 6 | - Please provide as much information as possible for us to replicate the issue. 7 | - Provide contextual information such OS, RN version, package version, etc. 8 | - Understand that while we will do our best to answer your issue, this is free support and we might not be able to resovle it immediately. 9 | 10 | ### When submitting PRs... 11 | - Provide as much details as possible with regard to the issue (include links if applicable) its resolving, the use cases, purpose, etc. 12 | - Update documentation files where applicable. 13 | - Help clean up... if you notice formating / style issues, feel free to help correct. 14 | - As with issues, we will do our best to review PRs in a timely manner but feel free to bump it if we haven't made it to it yet. 15 | - Keep in mind this is a wrapper for the underlying SDKs, and as such, we're not in the business of creating a whole new "SDK" on top of them. As much as possible, stick to the underlying methods. Helper methods are convenient, but they're not part of the core SDKs we're implementing. 16 | 17 | Thanks in advance for your help! 18 | -------------------------------------------------------------------------------- /docs/Constants.md: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | ## Usage 4 | ```JavaScript 5 | let { 6 | Constants 7 | } = require('react-native-twilio-chat') 8 | 9 | client.onSynchronizationStatusChanged = (status) => { 10 | if (status == Constants.TCHSynchronizationStatus.Completed) { 11 | console.log('Sync complete!') 12 | } 13 | } 14 | 15 | if (channel.status == Constants.TCHChannelStatus.Joined) { 16 | console.log('I can post!') 17 | } 18 | 19 | client.createChannel({ 20 | type: Constants.TCHChannelType.Private 21 | } 22 | ``` 23 | 24 | ### TCHChannelStatus 25 | - Invited 26 | - Joined 27 | - NotParticipating 28 | 29 | ### TCHChannelSynchronizationStatus 30 | - None 31 | - Identifier 32 | - Metadata 33 | - All 34 | - Failed 35 | 36 | ### TCHChannelType 37 | - Public 38 | - Private 39 | 40 | ### TCHClientConnectionState 41 | - Connecting 42 | - Connected 43 | - Disconnected 44 | - Denied 45 | - Error 46 | 47 | ### TCHClientSynchronizationStatus 48 | - Started 49 | - ChannelListCompleted 50 | - Completed 51 | - Failed 52 | 53 | ### TCHClientSynchronizationStrategy 54 | - All 55 | - ChannelsList 56 | 57 | ### TCHLogLevel 58 | - Fatal 59 | - Critical 60 | - Warning 61 | - Info 62 | - Debug 63 | 64 | ### TCHUserInfoUpdate 65 | - Attributes 66 | - FriendlyName 67 | - ReachabilityNotifiable 68 | - ReachabilityOnline 69 | 70 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/reactnativetwilioipmessagingexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnativetwilioipmessagingexample; 2 | 3 | 4 | import com.bradbumbalough.RCTTwilioChat.RCTTwilioChatPackage; 5 | import ca.jaysoo.extradimensions.ExtraDimensionsPackage; 6 | import android.app.Application; 7 | import android.util.Log; 8 | 9 | import com.facebook.react.ReactApplication; 10 | import com.facebook.react.ReactInstanceManager; 11 | import com.facebook.react.ReactNativeHost; 12 | import com.facebook.react.ReactPackage; 13 | import com.facebook.react.shell.MainReactPackage; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | 18 | public class MainApplication extends Application implements ReactApplication { 19 | 20 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 21 | @Override 22 | protected boolean getUseDeveloperSupport() { 23 | return BuildConfig.DEBUG; 24 | } 25 | 26 | @Override 27 | protected List getPackages() { 28 | return Arrays.asList( 29 | new MainReactPackage(), 30 | new RCTTwilioChatPackage() 31 | ); 32 | } 33 | }; 34 | 35 | @Override 36 | public ReactNativeHost getReactNativeHost() { 37 | return mReactNativeHost; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/UserInfo.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | NativeAppEventEmitter, 4 | } from 'react-native'; 5 | 6 | const { 7 | TwilioChatClient, 8 | } = NativeModules; 9 | 10 | class UserInfo { 11 | constructor(props) { 12 | this.identity = props.identity; 13 | this.friendlyName = props.friendlyName; 14 | this.attributes = props.attributes; 15 | this.isOnline = props.isOnline; 16 | this.isNotifiable = props.isNotifiable; 17 | 18 | this.onUpdate = null; 19 | 20 | // event handlers 21 | this._userInfoUpdateSubscription = NativeAppEventEmitter.addListener( 22 | 'chatClient:userInfoUpdated', 23 | ({ updated, userInfo }) => { 24 | if (userInfo.identity === this.identity) { 25 | this.friendlyName = userInfo.friendlyName; 26 | this.attributes = userInfo.attributes; 27 | this.isOnline = userInfo.isOnline; 28 | this.isNotifiable = userInfo.isNotifiable; 29 | if (this.onUpdate) this.onUpdate(updated); 30 | } 31 | }, 32 | ); 33 | } 34 | 35 | setAttributes(attributes) { 36 | return TwilioChatClient.setAttributes(attributes); 37 | } 38 | 39 | setFriendlyName(friendlyName) { 40 | return TwilioChatClient.setFriendlyName(friendlyName); 41 | } 42 | 43 | close() { 44 | this._userInfoUpdateSubscription.remove(); 45 | } 46 | } 47 | 48 | export default UserInfo; 49 | -------------------------------------------------------------------------------- /lib/Paginator.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | } from 'react-native'; 4 | 5 | import Channel from './Channel'; 6 | import Member from './Member'; 7 | 8 | const { TwilioChatPaginator } = NativeModules; 9 | 10 | export default class Paginator { 11 | constructor(sid, type, paginator) { 12 | this.sid = sid; 13 | this.type = type; 14 | this.hasNextPage = paginator.hasNextPage; 15 | 16 | let items = []; 17 | if (type === 'Channel' || type === 'ChannelDescriptor') { 18 | items = paginator.items.map(item => new Channel(item)); 19 | } else { 20 | items = paginator.items.map(item => new Member(item)); 21 | } 22 | this.items = items; 23 | } 24 | 25 | nextPage() { 26 | if (this.hasNextPage) { 27 | if (this.type === 'Channel') { 28 | return TwilioChatPaginator.requestNextPageChannels(this.sid) 29 | .then(this._returnNewPaginator); 30 | } 31 | else if (this.type === 'ChannelDescriptor') { 32 | return TwilioChatPaginator.requestNextPageChannelDescriptors(this.sid) 33 | .then(this._returnNewPaginator); 34 | } 35 | else { 36 | return TwilioChatPaginator.requestNextPageMembers(this.sid) 37 | .then(this._returnNewPaginator); 38 | } 39 | } 40 | } 41 | 42 | _returnNewPaginator({ sid, type, paginator }) { 43 | return new Paginator(sid, type, paginator); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /android/src/main/java/com/bradbumbalough/RCTTwilioChat/RCTTwilioChatPackage.java: -------------------------------------------------------------------------------- 1 | package com.bradbumbalough.RCTTwilioChat; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | 14 | public class RCTTwilioChatPackage implements ReactPackage { 15 | @Override 16 | public List> createJSModules() { 17 | return Collections.emptyList(); 18 | } 19 | 20 | @Override 21 | public List createViewManagers(ReactApplicationContext reactContext) { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createNativeModules( 27 | ReactApplicationContext reactContext) { 28 | List modules = new ArrayList<>(); 29 | 30 | modules.add(new RCTTwilioAccessManager(reactContext)); 31 | modules.add(new RCTTwilioChatClient(reactContext)); 32 | modules.add(new RCTTwilioChatChannels(reactContext)); 33 | modules.add(new RCTTwilioChatMembers(reactContext)); 34 | modules.add(new RCTTwilioChatMessages(reactContext)); 35 | modules.add(new RCTTwilioChatPaginator(reactContext)); 36 | 37 | return modules; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/AccessManager.md: -------------------------------------------------------------------------------- 1 | # AccessManager 2 | The AccessManager is Twilio's abstraction of authenticating your instance of Twilio from the functionality of Programable Chat. It is optional in the lastest release of the SDKs. 3 | 4 | ## `new AccessManager(token)` 5 | |Name |Type |Description | 6 | |--- |--- |--- | 7 | |*token*|String|The access token provided by your server 8 | 9 | ## Properties 10 | |Name |Type |Description | 11 | |--- |--- |--- | 12 | |*token*|String|The current token 13 | |*expires*|Date|The timestamp of when the token will expire 14 | 15 | ### Methods 16 | 17 | #### `registerClient()` **iOS Only** 18 | Call to attach the TwilioClient to the AccessManager so that `updateToken` automatically passes through. Otherwise, you'll need to update both. 19 | 20 | #### `removeListeners()` 21 | Call when unmounting or closing the Chat session. 22 | 23 | #### `updateToken(newToken)` 24 | Updates the token associated with the Access Manager. 25 | |Name |Type |Description | 26 | |--- |--- |--- | 27 | |*newToken*|String|A new token to renew your instance with 28 | 29 | ### Events 30 | You can specify handlers for events on the `accessManager` instance itself. For example, if you wanted to listen to the token expiration event, you would set `accessManager.onTokenExpired = function() { console.log('Token expired') }`. 31 | 32 | #### `onTokenExpired()` 33 | Fired when the current token has expired. 34 | 35 | #### `onTokenWillExpire()` 36 | Fired 3 minuts before the current token will expire. 37 | 38 | #### `onTokenInvalid()` 39 | Fired when the token provided to the manager is invalid. 40 | -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSAllowsArbitraryLoads 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/AccessManager.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | NativeAppEventEmitter, 4 | Platform 5 | } from 'react-native'; 6 | 7 | const { TwilioAccessManager } = NativeModules; 8 | 9 | function parseManager(manager) { 10 | if (manager) { 11 | this.token = manager.token; 12 | this.expires = new Date(manager.expirationDate); 13 | } 14 | return true; 15 | } 16 | 17 | class AccessManager { 18 | constructor(token) { 19 | this.token = token; 20 | 21 | this.onTokenExpired = null; 22 | this.onTokenWillExpire = null; 23 | this.onTokenInvalid = null; 24 | 25 | this._accessManagerTokenInvalidSubscription = NativeAppEventEmitter.addListener( 26 | 'accessManager:tokenInvalid', 27 | (error) => { 28 | if (this.onTokenInvalid) this.onTokenInvalid(error); 29 | }, 30 | ); 31 | 32 | this._accessManagerTokenWillExpireSubscription = NativeAppEventEmitter.addListener( 33 | 'accessManager:tokenWillExpire', 34 | (error) => { 35 | if (this.onTokenWillExpire) this.onTokenWillExpire(error); 36 | }, 37 | ); 38 | 39 | this._accessManagerTokenExpiredSubscription = NativeAppEventEmitter.addListener( 40 | 'accessManager:tokenExpired', 41 | () => { 42 | if (this.onTokenExpired) this.onTokenExpired(); 43 | }, 44 | ); 45 | 46 | TwilioAccessManager.accessManagerWithToken(this.token) 47 | .then(parseManager.bind(this)); 48 | } 49 | 50 | registerClient() { 51 | if (Platform.OS === 'ios') { 52 | TwilioAccessManager.registerClient(); 53 | } 54 | } 55 | 56 | updateToken(newToken) { 57 | TwilioAccessManager.updateToken(newToken) 58 | .then(parseManager.bind(this)); 59 | } 60 | 61 | removeListeners() { 62 | this._accessManagerTokenInvalidSubscription.remove(); 63 | this._accessManagerTokenExpiredSubscription.remove(); 64 | this._accessManagerTokenWillExpireSubscription.remove(); 65 | } 66 | } 67 | 68 | export default AccessManager; 69 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTConvert+TwilioChatClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTConvert+TwilioChatClient.h 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 5/31/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | #import 9 | #import 10 | #import 11 | #import 12 | 13 | @interface RCTConvert (TwilioChatClient) 14 | 15 | + (TCHClientSynchronizationStatus)TCHClientSynchronizationStatus:(id)json; 16 | + (TCHChannelSynchronizationStatus)TCHChannelSynchronizationStatus:(id)json; 17 | + (TCHChannelType)TCHChannelType:(id)json; 18 | + (TCHChannelStatus)TCHChannelStatus:(id)json; 19 | + (TCHUserInfoUpdate)TCHUserInfoUpdate:(id)json; 20 | + (TCHClientSynchronizationStrategy)TCHClientSynchronizationStrategy:(id)json; 21 | + (TCHLogLevel)TCHLogLevel:(id)json; 22 | + (TCHClientConnectionState)TCHClientConnectionState:(id)json; 23 | 24 | + (NSDictionary *)TwilioAccessManager:(TwilioAccessManager *)accessManager; 25 | + (NSDictionary *)TwilioChatClient:(TwilioChatClient *)client; 26 | 27 | + (NSDictionary *)TCHChannel:(TCHChannel *)channel; 28 | + (NSDictionary *)TCHChannelDescriptor:(TCHChannelDescriptor *)channel; 29 | + (NSDictionary *)TCHUserInfo:(TCHUserInfo *)userInfo; 30 | + (NSDictionary *)TCHMember:(TCHMember *)member; 31 | + (NSDictionary *)TCHMessage:(TCHMessage *)message; 32 | 33 | + (NSDictionary *)TCHMemberPaginator:(TCHMemberPaginator *)paginator; 34 | + (NSDictionary *)TCHChannelPaginator:(TCHChannelPaginator *)paginator; 35 | + (NSDictionary *)TCHChannelDescriptorPaginator:(TCHChannelDescriptorPaginator *)paginator; 36 | 37 | + (NSArray *)TCHChannels:(NSArray*)channels; 38 | + (NSArray *)TCHChannelDescriptors:(NSArray*)channels; 39 | + (NSArray *)TCHMembers:(NSArray*)members; 40 | + (NSArray *)TCHMessages:(NSArray *)messages; 41 | 42 | + (NSData *)dataWithHexString:(NSString*)hex; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTRootView.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | /** 21 | * Loading JavaScript code - uncomment the one you want. 22 | * 23 | * OPTION 1 24 | * Load from development server. Start the server from the repository root: 25 | * 26 | * $ npm start 27 | * 28 | * To run on device, change `localhost` to the IP address of your computer 29 | * (you can get this by typing `ifconfig` into the terminal and selecting the 30 | * `inet` value under `en0:`) and make sure your computer and iOS device are 31 | * on the same Wi-Fi network. 32 | */ 33 | 34 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; 35 | 36 | /** 37 | * OPTION 2 38 | * Load from pre-bundled file on disk. The static bundle is automatically 39 | * generated by the "Bundle React Native code and images" build step when 40 | * running the project on an actual device or running the project on the 41 | * simulator in the "Release" build configuration. 42 | */ 43 | 44 | // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 45 | 46 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 47 | moduleName:@"ReactNativeTwilioIPMessagingExample" 48 | initialProperties:nil 49 | launchOptions:launchOptions]; 50 | 51 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 52 | UIViewController *rootViewController = [UIViewController new]; 53 | rootViewController.view = rootView; 54 | self.window.rootViewController = rootViewController; 55 | [self.window makeKeyAndVisible]; 56 | return YES; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /Example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/src/main/java/com/bradbumbalough/RCTTwilioChat/RCTTwilioAccessManager.java: -------------------------------------------------------------------------------- 1 | package com.bradbumbalough.RCTTwilioChat; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.Promise; 5 | import com.facebook.react.bridge.WritableMap; 6 | import com.facebook.react.modules.core.DeviceEventManagerModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 9 | import com.facebook.react.bridge.ReactMethod; 10 | 11 | import com.twilio.accessmanager.AccessManager; 12 | 13 | 14 | public class RCTTwilioAccessManager extends ReactContextBaseJavaModule implements AccessManager.Listener { 15 | 16 | @Override 17 | public String getName() { 18 | return "TwilioAccessManager"; 19 | } 20 | 21 | public AccessManager accessManager = null; 22 | private ReactApplicationContext reactContext; 23 | 24 | public static RCTTwilioAccessManager rctTwilioAccessManager; 25 | 26 | public static RCTTwilioAccessManager getInstance() { 27 | return rctTwilioAccessManager; 28 | } 29 | 30 | public RCTTwilioAccessManager(ReactApplicationContext reactContext) { 31 | super(reactContext); 32 | this.reactContext = reactContext; 33 | rctTwilioAccessManager = this; 34 | } 35 | 36 | @ReactMethod 37 | public void accessManagerWithToken(String token, Promise promise) { 38 | RCTTwilioAccessManager tmp = RCTTwilioAccessManager.getInstance(); 39 | tmp.accessManager = new AccessManager(token, this); 40 | promise.resolve(RCTConvert.AccessManager(tmp.accessManager)); 41 | } 42 | 43 | @ReactMethod 44 | public void updateToken(String token, Promise promise) { 45 | RCTTwilioAccessManager tmp = RCTTwilioAccessManager.getInstance(); 46 | tmp.accessManager.updateToken(token); 47 | promise.resolve(RCTConvert.AccessManager(tmp.accessManager)); 48 | } 49 | 50 | @Override 51 | public void onTokenExpired(AccessManager twilioAccessManager) { 52 | reactContext 53 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 54 | .emit("accessManager:tokenExpired", null); 55 | } 56 | 57 | @Override 58 | public void onTokenWillExpire(AccessManager twilioAccessManager) { 59 | reactContext 60 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 61 | .emit("accessManager:tokenWillExpire", null); 62 | } 63 | 64 | @Override 65 | public void onError(AccessManager twilioAccessManager, String s) { 66 | WritableMap params = Arguments.createMap(); 67 | params.putString("error",s); 68 | reactContext 69 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 70 | .emit("accessManager:tokenInvalid",params); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioAccessManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioAccessManager.m 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 5/31/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTTwilioAccessManager.h" 10 | #import "RCTConvert+TwilioChatClient.h" 11 | #import 12 | #import "RCTTwilioChatClient.h" 13 | #import 14 | 15 | @interface RCTTwilioAccessManager() 16 | @end 17 | 18 | 19 | @implementation RCTTwilioAccessManager 20 | 21 | @synthesize bridge = _bridge; 22 | @synthesize accessManager; 23 | 24 | #pragma mark Singleton Methods 25 | 26 | + (instancetype)sharedManager { 27 | static RCTTwilioAccessManager *sharedMyManager = nil; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | sharedMyManager = [[self alloc] init]; 31 | }); 32 | return sharedMyManager; 33 | } 34 | 35 | RCT_EXPORT_MODULE() 36 | 37 | #pragma mark Twilio Access Manager Methods 38 | 39 | RCT_REMAP_METHOD(accessManagerWithToken, token:(NSString *)token token_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 40 | RCTTwilioAccessManager *_accessManager = [RCTTwilioAccessManager sharedManager]; 41 | _accessManager.accessManager = [TwilioAccessManager accessManagerWithToken:token delegate:self]; 42 | resolve([RCTConvert TwilioAccessManager:_accessManager.accessManager]); 43 | } 44 | 45 | RCT_REMAP_METHOD(updateToken, token:(NSString *)token update_token_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 46 | RCTTwilioAccessManager *_accessManager = [RCTTwilioAccessManager sharedManager]; 47 | [_accessManager.accessManager updateToken:token]; 48 | resolve([RCTConvert TwilioAccessManager:accessManager]); 49 | } 50 | 51 | RCT_EXPORT_METHOD(registerClient){ 52 | RCTTwilioAccessManager *_accessManager = [RCTTwilioAccessManager sharedManager]; 53 | __weak RCTTwilioChatClient *_client = [RCTTwilioChatClient sharedManager]; 54 | [_accessManager.accessManager registerClient:_client.client forUpdates:^(NSString * _Nonnull updatedToken) { 55 | [_client.client updateToken:updatedToken]; 56 | }]; 57 | } 58 | 59 | 60 | #pragma mark Twilio Access Manager Delagate Methods 61 | 62 | - (void)accessManagerTokenWillExpire:(TwilioAccessManager *)accessManager { 63 | NSLog(@"Access token will expire"); 64 | [self.bridge.eventDispatcher sendAppEventWithName:@"accessManager:tokenWillExpire" 65 | body:nil]; 66 | } 67 | 68 | - (void)accessManagerTokenExpired:(TwilioAccessManager *)accessManager { 69 | NSLog(@"Access token expired"); 70 | [self.bridge.eventDispatcher sendAppEventWithName:@"accessManager:tokenExpired" 71 | body:nil]; 72 | } 73 | 74 | - (void)accessManagerTokenInvalid:(nonnull TwilioAccessManager *)accessManager { 75 | NSLog(@"Token is invalid."); 76 | [self.bridge.eventDispatcher sendAppEventWithName:@"accessManager:tokenInvalid" 77 | body:nil]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | apply from: "../../node_modules/react-native/react.gradle" 6 | 7 | /** 8 | * Set this to true to create two separate APKs instead of one: 9 | * - An APK that only works on ARM devices 10 | * - An APK that only works on x86 devices 11 | * The advantage is the size of the APK is reduced by about 4MB. 12 | * Upload all the APKs to the Play Store and people will download 13 | * the correct one based on the CPU architecture of their device. 14 | */ 15 | def enableSeparateBuildPerCPUArchitecture = false 16 | 17 | /** 18 | * Run Proguard to shrink the Java bytecode in release builds. 19 | */ 20 | def enableProguardInReleaseBuilds = false 21 | 22 | android { 23 | compileSdkVersion 23 24 | buildToolsVersion "23.0.1" 25 | 26 | dexOptions { 27 | javaMaxHeapSize "2048M" 28 | } 29 | 30 | defaultConfig { 31 | applicationId "com.reactnativetwilioipmessagingexample" 32 | minSdkVersion 19 33 | targetSdkVersion 23 34 | versionCode 1 35 | versionName "1.0" 36 | multiDexEnabled true 37 | ndk { 38 | abiFilters "armeabi-v7a", "x86" 39 | } 40 | } 41 | splits { 42 | abi { 43 | reset() 44 | enable enableSeparateBuildPerCPUArchitecture 45 | universalApk false // If true, also generate a universal APK 46 | include "armeabi-v7a", "x86" 47 | } 48 | } 49 | buildTypes { 50 | release { 51 | minifyEnabled enableProguardInReleaseBuilds 52 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 53 | } 54 | } 55 | // applicationVariants are e.g. debug, release 56 | applicationVariants.all { variant -> 57 | variant.outputs.each { output -> 58 | // For each separate APK per architecture, set a unique version code as described here: 59 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 60 | def versionCodes = ["armeabi-v7a":1, "x86":2] 61 | def abi = output.getFilter(OutputFile.ABI) 62 | if (abi != null) { // null for the universal-debug, universal-release variants 63 | output.versionCodeOverride = 64 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 65 | } 66 | } 67 | } 68 | } 69 | 70 | dependencies { 71 | compile fileTree(dir: "libs", include: ["*.jar"]) 72 | compile "com.android.support:appcompat-v7:23.0.1" 73 | compile "com.facebook.react:react-native:+" // From node_modules 74 | compile project(':ExtraDimensions') 75 | compile project(':RCTTwilioChat') 76 | compile 'com.twilio:chat-android:0.12.1' 77 | compile 'com.twilio:accessmanager-android:0.1.0' 78 | } 79 | 80 | // Run this once to be able to run the application with BUCK 81 | // puts all compile dependencies into folder libs for BUCK to use 82 | task copyDownloadableDepsToLibs(type: Copy) { 83 | from configurations.compile 84 | into 'libs' 85 | } 86 | -------------------------------------------------------------------------------- /Example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*.web.js 5 | .*/*.android.js 6 | 7 | # Some modules have their own node_modules with overlap 8 | .*/node_modules/node-haste/.* 9 | 10 | # Ugh 11 | .*/node_modules/babel.* 12 | .*/node_modules/babylon.* 13 | .*/node_modules/invariant.* 14 | 15 | # Ignore react and fbjs where there are overlaps, but don't ignore 16 | # anything that react-native relies on 17 | .*/node_modules/fbjs/lib/Map.js 18 | .*/node_modules/fbjs/lib/ErrorUtils.js 19 | 20 | # Flow has a built-in definition for the 'react' module which we prefer to use 21 | # over the currently-untyped source 22 | .*/node_modules/react/react.js 23 | .*/node_modules/react/lib/React.js 24 | .*/node_modules/react/lib/ReactDOM.js 25 | 26 | .*/__mocks__/.* 27 | .*/__tests__/.* 28 | 29 | .*/commoner/test/source/widget/share.js 30 | 31 | # Ignore commoner tests 32 | .*/node_modules/commoner/test/.* 33 | 34 | # See https://github.com/facebook/flow/issues/442 35 | .*/react-tools/node_modules/commoner/lib/reader.js 36 | 37 | # Ignore jest 38 | .*/node_modules/jest-cli/.* 39 | 40 | # Ignore Website 41 | .*/website/.* 42 | 43 | # Ignore generators 44 | .*/local-cli/generator.* 45 | 46 | # Ignore BUCK generated folders 47 | .*\.buckd/ 48 | 49 | # Ignore RNPM 50 | .*/local-cli/rnpm/.* 51 | 52 | .*/node_modules/is-my-json-valid/test/.*\.json 53 | .*/node_modules/iconv-lite/encodings/tables/.*\.json 54 | .*/node_modules/y18n/test/.*\.json 55 | .*/node_modules/spdx-license-ids/spdx-license-ids.json 56 | .*/node_modules/spdx-exceptions/index.json 57 | .*/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json 58 | .*/node_modules/resolve/lib/core.json 59 | .*/node_modules/jsonparse/samplejson/.*\.json 60 | .*/node_modules/json5/test/.*\.json 61 | .*/node_modules/ua-parser-js/test/.*\.json 62 | .*/node_modules/builtin-modules/builtin-modules.json 63 | .*/node_modules/binary-extensions/binary-extensions.json 64 | .*/node_modules/url-regex/tlds.json 65 | .*/node_modules/joi/.*\.json 66 | .*/node_modules/isemail/.*\.json 67 | .*/node_modules/tr46/.*\.json 68 | 69 | 70 | [include] 71 | 72 | [libs] 73 | node_modules/react-native/Libraries/react-native/react-native-interface.js 74 | node_modules/react-native/flow 75 | flow/ 76 | 77 | [options] 78 | module.system=haste 79 | 80 | esproposal.class_static_fields=enable 81 | esproposal.class_instance_fields=enable 82 | 83 | experimental.strict_type_args=true 84 | 85 | munge_underscores=true 86 | 87 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 88 | 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' 89 | 90 | suppress_type=$FlowIssue 91 | suppress_type=$FlowFixMe 92 | suppress_type=$FixMe 93 | 94 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-6]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 95 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-6]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 96 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 97 | 98 | [version] 99 | ^0.26.0 100 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration from IP Messaging 2 | 3 | This new package contains breaking changes from it's predecessor, react-native-twilio-ip-messaging, mainly due the nature of how Twilio updated their SDKs with the product change from IP Messaging to Programmable Chat. 4 | 5 | I believe this is complete -- but if you come across any issues please let us know so we can update! 6 | 7 | ### Installation 8 | 9 | #### iOS 10 | - In your Podfile, remove reference to TwilioIPMessagingClient, TwilioCommon, and RCTTwilioIPMessaging. 11 | - Add reference to TwilioChatClient: `pod 'TwilioChatClient', '~> 0.16.0'` 12 | - Add reference to TwilioAccessManager, if desired: `pod 'TwilioAccessManager', '~> 0.1.1'` 13 | - Add reference to RCTTwilioChat: `pod 'RCTTwilioChat', :path => '../node_modules/react-native-twilio-ip-messaging/ios'` 14 | - Update your pod repositories with: `pod repo update` 15 | - Run `pod install` to perform the installation. This will remove the previous TwilioIPMessagingClient and TwilioCommon components and add the TwilioChatClient, RCTTwilioChat and optionally TwilioAccessManager. 16 | 17 | #### Android 18 | - Update your gradle imports to `compile "com.twilio:chat-android:0.11.0"` 19 | - Remove import of `twilio-common-android` if you had it previously 20 | - Update gradle.settins to use new Chat package: 21 | ```java 22 | include ':RCTTwilioChat', ':app' 23 | project(':RCTTwilioChat').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-ip-messaging/android') 24 | ``` 25 | - Update build.gradle to compile new package: `compile project(':RCTTwilioChat')` 26 | - Update MainApplication.java to include new Chat package: 27 | ```java 28 | import com.bradbumbalough.RCTTwilioChat.RCTTwilioChatPackage; 29 | ... 30 | new RCTTwilioChatPackage() 31 | ``` 32 | - Remove any references to `RCTTwilioIPMessaging` 33 | 34 | ### Access Manager 35 | - `AccessManager` is now optional, as you can directly construct a `Client` with the token. However, if you want lifecycle events such as when the token will expire, you will need the `AccessManager`. 36 | - The properties `isExpired` and `identity` are no longer part of an `AccessManager` instance. 37 | - A new event, `onTokenWillExpire`, has been added and is fired ~3 minutes before the current token will expire. 38 | - The event, `onError`, has been renamed to `onTokenInvalid`. 39 | 40 | ### Channel 41 | - Public channels not joined now contain `membersCount` and `messagesCount`. 42 | 43 | ### Client 44 | - Client is no longer constructed with an instance of `AccessManager`. Instead, you pass your `auth_token` into it. 45 | - New event `onClientConnectionStateChanged` added. 46 | - Channels are now obtained with `getUserChannels` and `getPublicChannels`. They return an instance of a `Paginator` that you use to iterate through the results. 47 | - `getChannels` has been removed. 48 | - `getChannel` now takes an sid or uniqueName. 49 | - `getChannelByUniqueName` and `getChannelBySid` were removed. 50 | - A `register` method was added to link the `Client` instance with an `AccessManager`. 51 | - `deregister` was removed. Use `unregister` instead. 52 | 53 | ### Constants 54 | - All constants were previously prefixed with `TWM`, and are now begun with `TCH`. 55 | - TCHChannelOption 56 | - TCHChannelStatus 57 | - TCHChannelSynchronizationStatus 58 | - TCHChannelType 59 | - TCHClientSynchronizationStatus 60 | - TCHClientSynchronizationStrategy 61 | - TCHLogLevel 62 | 63 | ### Member, Message, UserInfo 64 | - No changes -------------------------------------------------------------------------------- /Example/ios/GiftedMessengerExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatPaginator.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatPaginator.m 3 | // RCTTwilioChat 4 | // 5 | // Created by Brad Bumbalough on 12/2/16. 6 | // Copyright © 2016 Brad Bumbalough. All rights reserved. 7 | // 8 | 9 | #import "RCTTwilioChatPaginator.h" 10 | #import "RCTConvert+TwilioChatClient.h" 11 | #import 12 | 13 | @interface RCTTwilioChatPaginator() 14 | @end 15 | 16 | @implementation RCTTwilioChatPaginator 17 | 18 | @synthesize paginators; 19 | 20 | #pragma mark Singleton Methods 21 | 22 | + (id)sharedManager { 23 | static RCTTwilioChatPaginator *sharedMyManager = nil; 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | sharedMyManager = [[self alloc] init]; 27 | sharedMyManager.paginators = [[NSMutableDictionary alloc] init]; 28 | }); 29 | return sharedMyManager; 30 | } 31 | 32 | + (NSString*)setPaginator:(id)paginator { 33 | RCTTwilioChatPaginator *_paginator = [RCTTwilioChatPaginator sharedManager]; 34 | NSString *uuid = [[NSUUID UUID] UUIDString]; 35 | [_paginator.paginators setValue:paginator forKey:uuid]; 36 | return uuid; 37 | } 38 | 39 | RCT_EXPORT_MODULE() 40 | 41 | RCT_REMAP_METHOD(requestNextPageChannels, sid:(NSString*)sid requestNextPageChannels_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ 42 | NSMutableDictionary *_paginators = [[RCTTwilioChatPaginator sharedManager] paginators]; 43 | [[_paginators objectForKey:sid] requestNextPageWithCompletion:^(TCHResult *result, TCHChannelPaginator *paginator) { 44 | if (result.isSuccessful) { 45 | NSString* uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 46 | resolve(@{ 47 | @"sid":uuid, 48 | @"type": @"Channel", 49 | @"paginator": [RCTConvert TCHChannelPaginator:paginator] 50 | }); 51 | } 52 | else { 53 | reject(@"request-next-page", @"Error occured while attempting to request the next page.", result.error); 54 | } 55 | }]; 56 | } 57 | 58 | RCT_REMAP_METHOD(requestNextPageChannelDescriptors, sid:(NSString*)sid requestNextPageChannelDescriptors_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ 59 | NSMutableDictionary *_paginators = [[RCTTwilioChatPaginator sharedManager] paginators]; 60 | [[_paginators objectForKey:sid] requestNextPageWithCompletion:^(TCHResult *result, TCHChannelDescriptorPaginator *paginator) { 61 | if (result.isSuccessful) { 62 | NSString* uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 63 | resolve(@{ 64 | @"sid":uuid, 65 | @"type": @"ChannelDescriptor", 66 | @"paginator": [RCTConvert TCHChannelDescriptorPaginator:paginator] 67 | }); 68 | } 69 | else { 70 | reject(@"request-next-page", @"Error occured while attempting to request the next page.", result.error); 71 | } 72 | }]; 73 | } 74 | 75 | RCT_REMAP_METHOD(requestNextPageMembers, sid:(NSString*)sid requestNextPageMembers_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ 76 | NSMutableDictionary *_paginators = [[RCTTwilioChatPaginator sharedManager] paginators]; 77 | [[_paginators objectForKey:sid] requestNextPageWithCompletion:^(TCHResult *result, TCHMemberPaginator *paginator) { 78 | if (result.isSuccessful) { 79 | NSString* uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 80 | resolve(@{ 81 | @"sid":uuid, 82 | @"paginator": [RCTConvert TCHMemberPaginator:paginator] 83 | }); 84 | } 85 | else { 86 | reject(@"request-next-page", @"Error occured while attempting to request the next page.", result.error); 87 | } 88 | }]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Example/ios/ReactNativeTwilioIPMessagingExample.xcodeproj/xcuserdata/bbumbalough.xcuserdatad/xcschemes/ReactNativeTwilioIPMessagingExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatMembers.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatMembers.m 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/7/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTTwilioChatMembers.h" 10 | #import "RCTConvert+TwilioChatClient.h" 11 | #import "RCTTwilioChatClient.h" 12 | #import "RCTTwilioChatChannels.h" 13 | #import "RCTTwilioChatPaginator.h" 14 | #import 15 | 16 | @implementation RCTTwilioChatMembers 17 | 18 | RCT_EXPORT_MODULE() 19 | 20 | - (void)loadMembersFromChannelSid:(NSString *)sid :(void (^)(TCHResult *result, TCHMembers *members))completion { 21 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 22 | completion(result, [channel members]); 23 | }]; 24 | } 25 | 26 | #pragma mark Members Methods 27 | 28 | RCT_REMAP_METHOD(getMembers, channelSid:(NSString *)channelSid allObjects_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 29 | [self loadMembersFromChannelSid:channelSid :^(TCHResult *result, TCHMembers *members) { 30 | if (result.isSuccessful) { 31 | [members membersWithCompletion:^(TCHResult *result, TCHMemberPaginator *paginator) { 32 | if (result.isSuccessful) { 33 | NSString *uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 34 | resolve(@{ 35 | @"sid":uuid, 36 | @"type": @"Member", 37 | @"paginator": [RCTConvert TCHMemberPaginator:paginator] 38 | }); 39 | } 40 | else { 41 | reject(@"get-members-error", @"Error occured while attempting to get the members.", result.error); 42 | } 43 | }]; 44 | } 45 | else { 46 | reject(@"get-members-error", @"Error occured while attempting to get the members.", result.error); 47 | } 48 | }]; 49 | } 50 | 51 | RCT_REMAP_METHOD(add, channelSid:(NSString *)channelSid identity:(NSString *)identity add_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 52 | [self loadMembersFromChannelSid:channelSid :^(TCHResult *result, TCHMembers *members) { 53 | if (result.isSuccessful) { 54 | [members addByIdentity:identity completion:^(TCHResult *result) { 55 | if (result.isSuccessful) { 56 | resolve(@[@TRUE]); 57 | } 58 | else { 59 | reject(@"add-identity-error", @"Error occured while attempting to add a user to the channel.", result.error); 60 | } 61 | }]; 62 | } 63 | else { 64 | reject(@"add-identity-error", @"Error occured while attempting to add a user to the channel.", result.error); 65 | } 66 | }]; 67 | } 68 | 69 | RCT_REMAP_METHOD(invite, channelSid:(NSString *)channelSid identity:(NSString *)identity invite_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 70 | [self loadMembersFromChannelSid:channelSid :^(TCHResult *result, TCHMembers *members) { 71 | if (result.isSuccessful) { 72 | [members inviteByIdentity:identity completion:^(TCHResult *result) { 73 | if (result.isSuccessful) { 74 | resolve(@[@TRUE]); 75 | } 76 | else { 77 | reject(@"invite-identity-error", @"Error occured while attempting to inviate a user to the channel.", result.error); 78 | } 79 | }]; 80 | } 81 | else { 82 | reject(@"invite-identity-error", @"Error occured while attempting to inviate a user to the channel.", result.error); 83 | } 84 | }]; 85 | } 86 | 87 | RCT_REMAP_METHOD(remove, channelSid:(NSString *)channelSid identity:(NSString *)identity remove_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 88 | 89 | [RCTTwilioChatChannels loadChannelFromSid:channelSid :^(TCHResult *result, TCHChannel *channel) { 90 | TCHMember *member = [channel memberWithIdentity:identity]; 91 | [[channel members] removeMember:member completion:^(TCHResult *result) { 92 | if (result.isSuccessful) { 93 | resolve(@[@TRUE]); 94 | } 95 | else { 96 | reject(@"remove-member-error", @"Error occured while attempting to remove a user from the channel.", result.error); 97 | } 98 | }]; 99 | }]; 100 | } 101 | @end 102 | -------------------------------------------------------------------------------- /android/src/main/java/com/bradbumbalough/RCTTwilioChat/RCTTwilioChatPaginator.java: -------------------------------------------------------------------------------- 1 | package com.bradbumbalough.RCTTwilioChat; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | import com.facebook.react.bridge.ReactMethod; 6 | import com.facebook.react.bridge.Promise; 7 | 8 | import com.twilio.chat.Paginator; 9 | import com.twilio.chat.Channel; 10 | import com.twilio.chat.ChannelDescriptor; 11 | import com.twilio.chat.StatusListener; 12 | import com.twilio.chat.Member; 13 | import com.twilio.chat.CallbackListener; 14 | import com.twilio.chat.ErrorInfo; 15 | 16 | 17 | import java.util.UUID; 18 | import java.util.HashMap; 19 | 20 | public class RCTTwilioChatPaginator extends ReactContextBaseJavaModule { 21 | 22 | @Override 23 | public String getName() { 24 | return "TwilioChatPaginator"; 25 | } 26 | 27 | public static HashMap paginators = new HashMap(); 28 | // public static Map> channelDescriptorPaginators = new Map>(); 29 | // public static Map> memberPaginators = new Map>(); 30 | 31 | private static RCTTwilioChatPaginator rctTwilioChatPaginator; 32 | 33 | public static RCTTwilioChatPaginator getInstance() { 34 | return rctTwilioChatPaginator; 35 | } 36 | 37 | public RCTTwilioChatPaginator(ReactApplicationContext reactContext) { 38 | super(reactContext); 39 | rctTwilioChatPaginator = this; 40 | } 41 | 42 | public static String setPaginator(Object paginator) { 43 | RCTTwilioChatPaginator _paginator = RCTTwilioChatPaginator.getInstance(); 44 | String uuid = UUID.randomUUID().toString(); 45 | _paginator.paginators.put(uuid, paginator); 46 | return uuid; 47 | } 48 | 49 | @ReactMethod 50 | public void requestNextPageChannelDescriptors(String sid, final Promise promise) { 51 | final RCTTwilioChatPaginator tmp = RCTTwilioChatPaginator.getInstance(); 52 | Paginator _paginator = (Paginator)tmp.paginators.get(sid); 53 | 54 | _paginator.requestNextPage(new CallbackListener>() { 55 | @Override 56 | public void onError(ErrorInfo errorInfo) { 57 | super.onError(errorInfo); 58 | promise.reject("request-next-page", "Error occurred while attempting to request the next page. Error Message: " + errorInfo.getErrorText()); 59 | } 60 | 61 | @Override 62 | public void onSuccess(Paginator paginator) { 63 | String uuid = RCTTwilioChatPaginator.setPaginator(paginator); 64 | promise.resolve(RCTConvert.Paginator(paginator, uuid, "ChannelDescriptor")); 65 | } 66 | }); 67 | } 68 | 69 | public void requestNextPageChannels(String sid, final Promise promise) { 70 | final RCTTwilioChatPaginator tmp = RCTTwilioChatPaginator.getInstance(); 71 | Paginator _paginator = (Paginator)tmp.paginators.get(sid); 72 | 73 | _paginator.requestNextPage(new CallbackListener>() { 74 | @Override 75 | public void onError(ErrorInfo errorInfo) { 76 | super.onError(errorInfo); 77 | promise.reject("request-next-page", "Error occurred while attempting to request the next page. Error Message: " + errorInfo.getErrorText()); 78 | } 79 | 80 | @Override 81 | public void onSuccess(Paginator paginator) { 82 | String uuid = RCTTwilioChatPaginator.setPaginator(paginator); 83 | promise.resolve(RCTConvert.Paginator(paginator, uuid, "Channel")); 84 | } 85 | }); 86 | } 87 | 88 | public void requestNextPageMembers(String sid, final Promise promise) { 89 | final RCTTwilioChatPaginator tmp = RCTTwilioChatPaginator.getInstance(); 90 | Paginator _paginator = (Paginator)tmp.paginators.get(sid); 91 | 92 | _paginator.requestNextPage(new CallbackListener>() { 93 | @Override 94 | public void onError(ErrorInfo errorInfo) { 95 | super.onError(errorInfo); 96 | promise.reject("request-next-page", "Error occurred while attempting to request the next page. Error Message: " + errorInfo.getErrorText()); 97 | } 98 | 99 | @Override 100 | public void onSuccess(Paginator paginator) { 101 | String uuid = RCTTwilioChatPaginator.setPaginator(paginator); 102 | promise.resolve(RCTConvert.Paginator(paginator, uuid, "Member")); 103 | } 104 | }); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /Example/ios/ReactNativeTwilioIPMessagingExample.xcodeproj/xcshareddata/xcschemes/GiftedMessengerExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Twilio Chat 2 | [![npm version](https://badge.fury.io/js/react-native-twilio-chat.svg)](https://badge.fury.io/js/react-native-twilio-chat) 3 | 4 | >React Native wrapper for the Twilio Programmable Chat iOS and Android SDKs 5 | 6 | *Note - this project is currently in development for a beta release. If you are looking for the legacy package for the Twilio IP Messaging SDKs, [see the original repository here](https://github.com/ccm-innovation/react-native-twilio-ip-messaging).* 7 | 8 | ### [View migration doc from react-native-ip-messaging here](MIGRATION.md) 9 | 10 | ## Installation 11 | ``` 12 | npm install --save react-native-twilio-chat 13 | ``` 14 | 15 | ### iOS - CocoaPods 16 | Install the Twilio Chat SDK and this package via CocoaPods. See the [full Podfile example](./Example/ios/Podfile) for more details. 17 | 18 | ```ruby 19 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' 20 | pod 'React', :subspecs => ['Core', /* any other subspecs you require */], :path => '../node_modules/react-native' 21 | pod 'RCTTwilioChat', :path => '../node_modules/react-native-twilio-chat/ios' 22 | 23 | source 'https://github.com/twilio/cocoapod-specs' 24 | pod 'TwilioChatClient', '~> 0.17.1' 25 | pod 'TwilioAccessManager', '~> 0.1.3' 26 | ``` 27 | **Note: the underlying Twilio SDKs require a minimum deployment target of `8.1`**. If your project's target is less than this you will get a CocoaPods install error (`Unable to satisfy the following requirements...`). 28 | 29 | Make sure that you add the `$(inherited)` value to `Other Linker Flags` and `Framework Search Paths` for your target's Build Settings. This is also assuming you have already loaded React via CocoaPods as well. 30 | 31 | ### Android 32 | In `android/settings.gradle`: 33 | 34 | ```java 35 | include ':RCTTwilioChat', ':app' 36 | project(':RCTTwilioChat').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-chat/android') 37 | ``` 38 | 39 | In `android/app/build.gradle`: 40 | ```java 41 | ... 42 | dependencies { 43 | ... 44 | compile project(':RCTTwilioChat') 45 | } 46 | 47 | ``` 48 | 49 | Register the module in `MainApplication.java`: 50 | ```Java 51 | // import package 52 | import com.bradbumbalough.RCTTwilioChat.RCTTwilioChatPackage; 53 | 54 | ... 55 | 56 | // register package in getPackages() 57 | @Override 58 | protected List getPackages() { 59 | return Arrays.asList( 60 | new MainReactPackage(), 61 | new RCTTwilioChatPackage(), 62 | ... other packages 63 | ); 64 | } 65 | ``` 66 | 67 | **Note:** You might have to enable multidex in your `build.gradle` file and increase the heap size if you're getting errors while buliding. The minSdkVersion must also be at least 19, per the Twilio SDKs. 68 | 69 | ```java 70 | android { 71 | .... 72 | dexOptions { 73 | javaMaxHeapSize "2048M" 74 | } 75 | 76 | defaultConfig { 77 | ... 78 | minSdkVersion 19 79 | multiDexEnabled true 80 | } 81 | ``` 82 | 83 | ## Usage 84 | ```javascript 85 | /* Initialization */ 86 | 87 | import { 88 | AccessManager, 89 | Client, 90 | Constants 91 | } from 'react-native-twilio-chat'; 92 | 93 | // create the access manager 94 | const accessManager = new AccessManager(token); 95 | 96 | // specify any handlers for events 97 | accessManager.onTokenWillExpire = () => { 98 | getNewTokenFromServer() 99 | .then(accessManager.updateToken); 100 | } 101 | 102 | // create the client 103 | const client = new Client(token); 104 | 105 | // specify any global events 106 | client.onError = ({error, userInfo}) => console.log(error); 107 | 108 | // initialize the client 109 | client.initialize(); 110 | 111 | // wait for sync to finish 112 | client.onClientSynchronized = () => { 113 | client.getUserChannels() 114 | .then((channelPaginator) => console.log(channelPaginator.items)); 115 | } 116 | 117 | /* Individual Channel */ 118 | 119 | // an instance of Channel is passed down in the app via props 120 | let channel = this.props.channel 121 | 122 | // specify channel specific events 123 | channel.onMessageAdded = (message) => console.log(message.author + ": " + message.body); 124 | channel.onTypingStarted = (member) => console.log(member.identity + " started typing..."); 125 | channel.onTypingEnded = (member) => console.log(member.identity + " stopped typing..."); 126 | channel.onMemberAdded = (member) => console.log(member.identity + " joined " + channel.friendlyName); 127 | 128 | // sending a message 129 | { 131 | this.setState({body}); 132 | channel.typing(); 133 | }} 134 | onSubmitEditing={() => { channel.sendMessage(this.state.body)} } 135 | /> 136 | ```` 137 | 138 | ## [Documentation](docs) 139 | 140 | ## Contributers 🍻 141 | Thank you for your help in maintaining this project! Haven't contributed yet? [Check out our Contributing guidelines...](CONTRIBUTING.md). 142 | - [bradbumbalough](https://github.com/bradbumbalough) 143 | - [johndrkurtcom](https://github.com/johndrkurtcom) 144 | - [jck2](https://github.com/jck2) 145 | - [Baisang](https://github.com/Baisang) 146 | - [thathirsch](https://github.com/thathirsch) 147 | - [n8stowell82](https://github.com/n8stowell82) 148 | - [svlaev](https://github.com/svlaev) 149 | - [Maxwell2022](https://github.com/Maxwell2022) 150 | - [bbil](https://github.com/bbil) 151 | - [jhabdas](https://github.com/jhabdas) 152 | - [plonkus](https://github.com/plonkus) 153 | - [mattshen](https://github.com/mattshen) 154 | - [Kabangi](https://github.com/Kabangi) 155 | - [benoist](https://github.com/benoist) 156 | 157 | ## TODO 🗒 158 | * [x] Copy code from `programable-chat` branch on old package 159 | * [x] Copy issues and PRs over 160 | * [x] Update docs (wiki?) 161 | * [x] Migration guide 162 | * [x] Publish to npm 163 | * [x] Update `twilio-ip-messaging` to reference `twilio-chat` 164 | * [ ] 1.0 release 165 | * [ ] Testing 166 | 167 | ## License 168 | This project is licensed under the [MIT License](LICENSE). 169 | -------------------------------------------------------------------------------- /android/src/main/java/com/bradbumbalough/RCTTwilioChat/RCTTwilioChatMembers.java: -------------------------------------------------------------------------------- 1 | package com.bradbumbalough.RCTTwilioChat; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | import com.facebook.react.bridge.ReactMethod; 6 | import com.facebook.react.bridge.Promise; 7 | 8 | import com.twilio.chat.Member; 9 | import com.twilio.chat.StatusListener; 10 | import com.twilio.chat.ErrorInfo; 11 | import com.twilio.chat.Members; 12 | import com.twilio.chat.CallbackListener; 13 | import com.twilio.chat.Channel; 14 | import com.twilio.chat.Paginator; 15 | 16 | import java.sql.Array; 17 | import java.util.ArrayList; 18 | 19 | 20 | public class RCTTwilioChatMembers extends ReactContextBaseJavaModule { 21 | 22 | @Override 23 | public String getName() { 24 | return "TwilioChatMembers"; 25 | } 26 | 27 | 28 | public RCTTwilioChatMembers(ReactApplicationContext reactContext) { 29 | super(reactContext); 30 | } 31 | 32 | private void loadMembersFromChannelSid(String sid, final CallbackListener callbackListener) { 33 | RCTTwilioChatClient.getInstance().client.getChannels().getChannel(sid, new CallbackListener() { 34 | @Override 35 | public void onSuccess(final Channel channel) { 36 | callbackListener.onSuccess(channel.getMembers()); 37 | }; 38 | 39 | @Override 40 | public void onError(final ErrorInfo errorInfo){ 41 | callbackListener.onError(errorInfo); 42 | } 43 | }); 44 | } 45 | 46 | @ReactMethod 47 | public void getMembers(String channelSid, final Promise promise) { 48 | loadMembersFromChannelSid(channelSid, new CallbackListener() { 49 | @Override 50 | public void onError(ErrorInfo errorInfo) { 51 | super.onError(errorInfo); 52 | promise.reject("get-members-error","Error occurred while attempting to get members on channel."); 53 | } 54 | 55 | @Override 56 | public void onSuccess(Members members) { 57 | members.getMembers(new CallbackListener>() { 58 | @Override 59 | public void onError(ErrorInfo errorInfo) { 60 | super.onError(errorInfo); 61 | promise.reject("get-members-error","Error occurred while attempting to get members on channel."); 62 | } 63 | 64 | @Override 65 | public void onSuccess(Paginator memberPaginator) { 66 | String uuid = RCTTwilioChatPaginator.setPaginator(memberPaginator); 67 | promise.resolve(RCTConvert.Paginator(memberPaginator, uuid, "Member")); 68 | } 69 | }); 70 | } 71 | }); 72 | } 73 | 74 | @ReactMethod 75 | public void add(String channelSid, final String identity, final Promise promise) { 76 | loadMembersFromChannelSid(channelSid, new CallbackListener() { 77 | @Override 78 | public void onError(ErrorInfo errorInfo) { 79 | super.onError(errorInfo); 80 | promise.reject("add-error","Error occurred while attempting to add user to channel."); 81 | } 82 | 83 | @Override 84 | public void onSuccess(Members members) { 85 | members.addByIdentity(identity, new StatusListener() { 86 | @Override 87 | public void onError(ErrorInfo errorInfo) { 88 | super.onError(errorInfo); 89 | promise.reject("add-error","Error occurred while attempting to add user to channel."); 90 | } 91 | 92 | @Override 93 | public void onSuccess() { 94 | promise.resolve(true); 95 | } 96 | }); 97 | } 98 | }); 99 | } 100 | 101 | @ReactMethod 102 | public void invite(String channelSid, final String identity, final Promise promise) { 103 | loadMembersFromChannelSid(channelSid, new CallbackListener() { 104 | @Override 105 | public void onError(ErrorInfo errorInfo) { 106 | super.onError(errorInfo); 107 | promise.reject("invite-error","Error occurred while attempting to invite user to channel."); 108 | } 109 | 110 | @Override 111 | public void onSuccess(Members members) { 112 | members.inviteByIdentity(identity, new StatusListener() { 113 | @Override 114 | public void onError(ErrorInfo errorInfo) { 115 | super.onError(errorInfo); 116 | promise.reject("invite-error","Error occurred while attempting to invite user to channel."); 117 | } 118 | 119 | @Override 120 | public void onSuccess() { 121 | promise.resolve(true); 122 | } 123 | }); 124 | } 125 | }); 126 | } 127 | 128 | @ReactMethod 129 | public void remove(String channelSid, final String identity, final String paginatorSid, final Promise promise) { 130 | loadMembersFromChannelSid(channelSid, new CallbackListener() { 131 | @Override 132 | public void onError(ErrorInfo errorInfo) { 133 | super.onError(errorInfo); 134 | promise.reject("remove-error","Error occurred while attempting to remove user from channel."); 135 | } 136 | 137 | @Override 138 | public void onSuccess(Members members) { 139 | RCTTwilioChatPaginator _paginator = RCTTwilioChatPaginator.getInstance(); 140 | ArrayList memberList = ((Paginator)_paginator.paginators.get(paginatorSid)).getItems(); 141 | Member memberToDelete = null; 142 | for (Member m : memberList) { 143 | if (m.getUserInfo().getIdentity() == identity) { 144 | memberToDelete = m; 145 | } 146 | } 147 | members.removeMember(memberToDelete, new StatusListener() { 148 | @Override 149 | public void onError(ErrorInfo errorInfo) { 150 | super.onError(errorInfo); 151 | promise.reject("remove-error","Error occurred while attempting to remove user from channel."); 152 | } 153 | 154 | @Override 155 | public void onSuccess() { 156 | promise.resolve(true); 157 | } 158 | }); 159 | } 160 | }); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /docs/Channel.md: -------------------------------------------------------------------------------- 1 | # Channel 2 | A class that handles the interactions with a specific channel. 3 | 4 | ## Usage 5 | ```JavaScript 6 | let channel = this.props.channel 7 | 8 | // specify channel specific events 9 | channel.onMessageAdded = (message) => console.log(message.author + ": " + message.body); 10 | channel.onTypingStarted = (member) => console.log(member.identity + " started typing..."); 11 | channel.onTypingEnded = (member) => console.log(member.identity + " stopped typing..."); 12 | channel.onMemberAdded = (member) => console.log(member.identity + " joined " + channel.friendlyName); 13 | 14 | channel.getMessages(20) 15 | .then((messages) => { 16 | // array of message instances 17 | console.log(messages) 18 | } 19 | 20 | // mark all messages as read, etc 21 | channel.setAllMessagesConsumed() 22 | 23 | // sending a message 24 | { 26 | this.setState({body}); 27 | channel.typing(); 28 | }} 29 | onSubmitEditing={() => channel.sendMessage(this.state.body)} 30 | /> 31 | ``` 32 | 33 | ## Properties 34 | |Name |Type |Description | 35 | |--- |--- |--- | 36 | |*sid*|String|The sid of the channel (shouldn't need this in an instance, all methods are pre-bound) 37 | |*friendlyName*|String|Friendly name of the channel 38 | |*uniqueName*|String|Unique name of the channel 39 | |*attributes*|Object|Any custom attributes added to the channel 40 | |*synchronizationStatus*|Constants.TCHChannelSynchronizationStatus|Current synchronization status of the channel 41 | |*status*|Constants.TCHChannelStatus|The user's association with the channel 42 | |*type*|Constants.TCHChannelType|Whether the channel is public or private 43 | |*dateCreated*|Date|When the channel was created 44 | |*dateUpdated*|Date|When the channel was last updated 45 | |*createdBy*|String|The identity of the channel creator 46 | 47 | *On public channels accessed through getPublicChannels, you will have these additional properties* 48 | 49 | |Name |Type |Description | 50 | |--- |--- |--- | 51 | |*messagesCount*|Number|Count of messages 52 | |*membersCount*|Number|Count of members 53 | 54 | ## Methods 55 | 56 | #### `advanceLastConsumedMessageIndex(index)` 57 | |Name |Type |Description | 58 | |--- |--- |--- | 59 | |*index*|Number|The index of the message consumed (should be greater than last consumed index) 60 | 61 | #### `add(identity)` : Promise 62 | |Name |Type |Description | 63 | |--- |--- |--- | 64 | |*identity*|String|The identity of the user to add (without inviting) 65 | 66 | #### `close()` 67 | Close the channel and remove all listeners (call in `componentWillUnmount`). 68 | 69 | #### `declineInvitation()` : Promise 70 | Decline joining the channel, in reply to an invitation. 71 | 72 | #### `destroy()` : Promise 73 | Delete a channel. 74 | 75 | #### `getLastConsumedMessageIndex()` : Promise 76 | Returns `Number` index. 77 | 78 | #### `getMember(identity)` : Promise 79 | |Name |Type |Description | 80 | |--- |--- |--- | 81 | |*identity*|String|The identity of the user to return 82 | Returns a `Member` instance. 83 | 84 | #### `getMembers()` : Promise 85 | Returns an `Array` instances. 86 | 87 | #### `getMembersCount()` : Promise 88 | Returns the number of members for this channel. 89 | 90 | #### `getMessage(index)` : Promise 91 | |Name |Type |Description | 92 | |--- |--- |--- | 93 | |*index*|Number|The index of the message to get 94 | 95 | #### `getMessages(count = 10)` : Promise 96 | |Name |Type |Description | 97 | |--- |--- |--- | 98 | |*count*|Number|Default 10. The number of most recent messages to get 99 | Returns an `Array` instances. 100 | 101 | #### `getMessagesAfter(index, count)` : Promise 102 | |Name |Type |Description | 103 | |--- |--- |--- | 104 | |*index*|Number|The starting point index 105 | |*count*|Number|The number of succeeding messages to return 106 | Returns an `Array` instances. 107 | 108 | #### `getMessagesBefore(index, count)` : Promise 109 | |Name |Type |Description | 110 | |--- |--- |--- | 111 | |*index*|Number|The starting point index 112 | |*count*|Number|The number of preceding messages to return 113 | Returns an `Array` instances. 114 | 115 | #### `getMessageForConsumption(index)` : Promise **(iOS Only)** 116 | |Name |Type |Description | 117 | |--- |--- |--- | 118 | |*index*|Number|The index of the last message reported as read (may refer to a deleted message) 119 | 120 | #### `getMessagesCount()` : Promise 121 | Returns the number of messages for this channel. 122 | 123 | #### `getUnconsumedMessagesCount()` : Promise 124 | Returns the number of unread messages for this channel. 125 | 126 | #### `initialize()` : Promise 127 | Synchronize the channel with the server. May not be needed depending on if you set synchronizationStrategy to `All` during the client initialization. Otherwise, without calling `initialize` you won't get notificed when any events pertaining to this channel occur. 128 | 129 | #### `invite(identity)` : Promise 130 | |Name |Type |Description | 131 | |--- |--- |--- | 132 | |*identity*|String|The identity of the user to invite 133 | 134 | #### `join()` : Promise 135 | Join the channel (if not a member or in reply to an invitation). 136 | 137 | #### `leave()` : Promise 138 | Leave a channel. 139 | 140 | #### `remove(identity)` : Promise 141 | |Name |Type |Description | 142 | |--- |--- |--- | 143 | |*identity*|String|The identity of the user to remove 144 | 145 | #### `removeMessage(index)` : Promise 146 | |Name |Type |Description | 147 | |--- |--- |--- | 148 | |*index*|Number|The index of the message to delete 149 | 150 | #### `sendMessage(body, attributes)` : Promise 151 | |Name |Type |Description | 152 | |--- |--- |--- | 153 | |*body*|String|The message body 154 | |*attributes*|Object|Any properties you want associated with the message (Optional) 155 | 156 | #### `setAllMessagesConsumed()` 157 | Update the last consumed index for this Member and Channel to the max message currently on this device. 158 | 159 | #### `setAttributes(attributes)` : Promise 160 | |Name |Type |Description | 161 | |--- |--- |--- | 162 | |*attributes*|Object|Any properties you want associated with the channel 163 | 164 | #### `setFriendlyName(friendlyName)` : Promise 165 | |Name |Type |Description | 166 | |--- |--- |--- | 167 | |*friendlyName*|String|Specify a friendly name for the channel 168 | 169 | #### `setLastConsumedMessageIndex(index)` 170 | |Name |Type |Description | 171 | |--- |--- |--- | 172 | |*index*|Number|The index of the consumed message 173 | Returns a `Message` instance. 174 | 175 | #### `setUniqueName(uniqueName)` : Promise 176 | |Name |Type |Description | 177 | |--- |--- |--- | 178 | |*uniqueName*|String|Specify a unique name for the channel 179 | 180 | #### `typing()` 181 | Invoke whenever the user is typing a message. 182 | 183 | ## Events 184 | 185 | #### `onChanged()` 186 | 187 | #### `onDeleted()` 188 | 189 | #### `onMemberChanged(member)` 190 | |Name |Type |Description | 191 | |--- |--- |--- | 192 | |*member*|Member|The changed member instance 193 | 194 | #### `onMemberJoined(member)` 195 | |Name |Type |Description | 196 | |--- |--- |--- | 197 | |*member*|Member|The instance of the new member 198 | 199 | #### `onMemberLeft(member)` 200 | |Name |Type |Description | 201 | |--- |--- |--- | 202 | |*member*|Member|The member instance who left 203 | 204 | #### `onMemberUserInfoUpdated({updated, userInfo})` **iOS Only** 205 | |Name |Type |Description | 206 | |--- |--- |--- | 207 | |*updated*|Constants.TCHUserInfoUpdated|The type of userInfo update (**iOS Only**) 208 | |*userInfo*|UserInfo|The new UserInfo instance 209 | 210 | #### `onMessageAdded(message)` 211 | |Name |Type |Description | 212 | |--- |--- |--- | 213 | |*message*|Message|The instance of the new message 214 | 215 | #### `onMessageChanged(message)` 216 | |Name |Type |Description | 217 | |--- |--- |--- | 218 | |*message*|Message|The instance of the changed message 219 | 220 | #### `onMessageDeleted(message)` 221 | |Name |Type |Description | 222 | |--- |--- |--- | 223 | |*message*|Message|The instance of the deleted message 224 | 225 | #### `onSynchronizationStatusChanged(status)` 226 | |Name |Type |Description | 227 | |--- |--- |--- | 228 | |*status*|Constants.TCHChannelSynchronizationStatus|The new synchronization status of the channel 229 | 230 | #### `onToastReceived(message)` 231 | |Name |Type |Description | 232 | |--- |--- |--- | 233 | |*message*|Message|The instance of the toast message 234 | 235 | #### `onTypingStarted(member)` 236 | |Name |Type |Description | 237 | |--- |--- |--- | 238 | |*member*|Member|The member who started typing 239 | 240 | #### `onTypingEnded(member)` 241 | |Name |Type |Description | 242 | |--- |--- |--- | 243 | |*member*|Member|The member who ended typing -------------------------------------------------------------------------------- /docs/Client.md: -------------------------------------------------------------------------------- 1 | # Client 2 | The Client is the main interface for interacting with the Twilio SDKs. 3 | 4 | ## Usage 5 | ```JavaScript 6 | // create the client 7 | const client = new Client(token); 8 | 9 | // specify any global events 10 | client.onError = ({error, userInfo}) => console.log(error); 11 | 12 | // initialize the client 13 | client.initialize(); 14 | 15 | // wait for sync to finish 16 | client.onClientSynchronized = () => { 17 | client.getUserChannels() 18 | .then((channelPaginator) => console.log(channelPaginator)); 19 | 20 | // create a new channel 21 | client.createChannel({ 22 | friendlyName: 'My Channel', 23 | uniqueName: 'my_channel', 24 | type: Constants.TCHChannelType.Private 25 | }) 26 | .then((channel) => console.log(channel)); 27 | } 28 | ``` 29 | 30 | ## `new Client(token[, synchronizationStrategy[, initialMessageCount]])` 31 | |Name |Type |Description | 32 | |--- |--- |--- | 33 | |*token*|String|The Access Token provided by your server 34 | |*synchronizationStrategy*|Constants.TCHClientSynchronizationStrategy|Optional. The synchronization strategy to use during client initialization. Default: ChannelsList [See Twilio Docs](https://media.twiliocdn.com/sdk/ios/chat/releases/0.17.1/docs/Constants/TCHClientSynchronizationStrategy.html) 35 | |*initialMessageCount*|Number|Optional. The number of most recent messages to fetch automatically when synchronizing a channel. Default: 100 36 | 37 | ## Properties 38 | |Name |Type |Description | 39 | |--- |--- |--- | 40 | |*userInfo*|UserInfo|The current user properties 41 | |*version*|String|The version of the SDK 42 | |*synchronizationStatus*|Constants.TCHClientSynchronizationStatus|The current status of the client's initialization 43 | |*isReachabilityEnabled*|Boolean|Whether or not reachability has been enabled for the messaging instance 44 | 45 | ## Methods 46 | 47 | #### `createChannel(options)` : Promise 48 | |Name |Type |Description | 49 | |--- |--- |--- | 50 | |*options*|Object|Specify the options of the channel you're creating (see below) 51 | 52 | **Options** 53 | 54 | |Name |Type |Description | 55 | |--- |--- |--- | 56 | |*friendlyName*|String|Optional. Friendly name of channel 57 | |*uniqueName*|String|Optional. Unique name of channel 58 | |*type*|Constants.TCHChannelType|Optional. Whether the channel will be private or public (default) 59 | |*attributes*|Object|Optional. Attributes to attach to the channel 60 | 61 | Create a new channel. Returns `Channel`. 62 | 63 | #### `getChannel(sid)` : Promise 64 | |Name |Type |Description | 65 | |--- |--- |--- | 66 | |*sid*|String|Sid of the channel to return 67 | Get a single instance of a Channel. Returns `Channel`. 68 | 69 | #### `getPublicChannels()` : Promise 70 | Get all of the public channels. Returns an instance of `Paginator`. 71 | 72 | #### `getUserChannels()` : Promise 73 | Get all of the user's channels. Returns an instance of `Paginator`. 74 | 75 | #### `handleNotification(notification)` 76 | Queue the incoming notification with the messaging library for processing - for React Native, this will come in `PushNotificationIOS.addEventListener('notification', handleNotification)`. 77 | 78 | |Name |Type |Description | 79 | |--- |--- |--- | 80 | |*notification*|Object|The incoming notification. 81 | 82 | #### `initialize()` 83 | Initialize the Client with the provided Access Manager and begin synchronization. 84 | 85 | #### `register(token)` 86 | Register APNS token for push notifications. This can be obtained in `PushNotificationIOS.addListener('register', handler)`. 87 | 88 | |Name |Type |Description | 89 | |--- |--- |--- | 90 | |*token*|String|The APNS token which usually comes from ‘didRegisterForRemoteNotificationsWithDeviceToken’. 91 | 92 | #### `setLogLevel(logLevel)` 93 | |Name |Type |Description | 94 | |--- |--- |--- | 95 | |*logLevel*|Constants.TCHLogLevel|Set the log level of the SDK 96 | 97 | #### `shutdown()` 98 | Terminate the instance of the client, and remove all the listeners. Note: this does not remove channel specific listeners. 99 | 100 | #### `unregister(token)` 101 | Unregister from push notification updates. 102 | 103 | |Name |Type |Description | 104 | |--- |--- |--- | 105 | |*token*|String|The APNS token which usually comes from ‘didRegisterForRemoteNotificationsWithDeviceToken’. 106 | 107 | ### Events 108 | Instead of having to worry about creating native listeners, simply specify handlers on the client instance for the events you want to be notified about. 109 | 110 | #### `onChannelAdded(channel)` 111 | |Name |Type |Description | 112 | |--- |--- |--- | 113 | |*channel*|Channel|An instance of the new channel 114 | 115 | #### `onChannelChanged(channel)` 116 | |Name |Type |Description | 117 | |--- |--- |--- | 118 | |*channel*|Channel|An instance of the changed channel 119 | 120 | #### `onChannelDeleted(channel)` 121 | |Name |Type |Description | 122 | |--- |--- |--- | 123 | |*channel*|Channel|An instance of the deleted channel 124 | 125 | #### `onChannelInvited(channel)` 126 | |Name |Type |Description | 127 | |--- |--- |--- | 128 | |*channel*|Channel|An instance of the changed channel 129 | 130 | #### `onChannelSynchronizationStatusChanged({channelSid, status})` 131 | |Name |Type |Description | 132 | |--- |--- |--- | 133 | |*channelSid*|String|The sid of the channel 134 | |*status*|Constants.TCHChannelSynchronizationStatus|The synchronization status of the channel 135 | 136 | #### `onClientConnectionStateChanged(state)` 137 | |Name |Type |Description | 138 | |--- |--- |--- | 139 | |*status*|Constants.TCHClientConnectionState|The client's connection state 140 | 141 | #### `onClientSynchronized()` 142 | Fired when the client has finished synchronizing and populated all of its attributes. 143 | 144 | #### `onError({error, userId})` 145 | |Name |Type |Description | 146 | |--- |--- |--- | 147 | |*error*|String|The error message from the SDK 148 | |*userInfo*|Object|The Error's userInfo method object 149 | 150 | #### `onMemberChanged({channelSid, member})` 151 | |Name |Type |Description | 152 | |--- |--- |--- | 153 | |*channelSid*|String|The sid of the channel 154 | |*member*|Object|The changed member 155 | 156 | #### `onMemberJoined({channelSid, member})` 157 | |Name |Type |Description | 158 | |--- |--- |--- | 159 | |*channelSid*|String|The sid of the channel 160 | |*member*|Object|The joined member 161 | 162 | #### `onMemberLeft({channelSid, member})` 163 | |Name |Type |Description | 164 | |--- |--- |--- | 165 | |*channelSid*|String|The sid of the channel 166 | |*member*|Object|The left member 167 | 168 | #### `onMemberUserInfoUpdated({updated, userInfo})` **iOS Only** 169 | |Name |Type |Description | 170 | |--- |--- |--- | 171 | |*channelSid*|String|The Sid of the channel the member is part of 172 | |*updated*|Constants.TCHUserInfoUpdated|The type of userInfo update (**iOS Only**) 173 | |*userInfo*|UserInfo|The new UserInfo instance 174 | 175 | #### `onMessageAdded({channelSid, message})` 176 | |Name |Type |Description | 177 | |--- |--- |--- | 178 | |*channelSid*|String|The sid of the channel 179 | |*message*|Message|The instance of the new Message 180 | 181 | #### `onMessageChanged({channelSid, message})` 182 | |Name |Type |Description | 183 | |--- |--- |--- | 184 | |*channelSid*|String|The sid of the channel 185 | |*message*|Message|The instance of the changed Message 186 | 187 | #### `onMessageDeleted({channelSid, message})` 188 | |Name |Type |Description | 189 | |--- |--- |--- | 190 | |*channelSid*|String|The sid of the channel 191 | |*message*|Message|The instance of the deleted Message 192 | 193 | #### `onSynchronizationStatusChanged(status)` 194 | |Name |Type |Description | 195 | |--- |--- |--- | 196 | |*status*|Constants.TCHClientSynchronizationStatus|The client's synchronization status 197 | 198 | #### `onToastReceived({channelSid, messageSid})` 199 | |Name |Type |Description | 200 | |--- |--- |--- | 201 | |*channelSid*|String|The sid of the channel 202 | |*messageSid*|String|The message sid (if applicable) 203 | 204 | #### `onToastSubscribed()` 205 | 206 | #### `onTypingEnded({channelSid, member})` 207 | |Name |Type |Description | 208 | |--- |--- |--- | 209 | |*channelSid*|String|The sid of the channel 210 | |*member*|Object|The member who ended typing 211 | 212 | #### `onToastFailed({error, userId})` 213 | |Name |Type |Description | 214 | |--- |--- |--- | 215 | |*error*|String|The error message from the SDK 216 | |*userInfo*|Object|The Error's userInfo method object 217 | 218 | 219 | #### `onTypingStarted({channelSid, member})` 220 | |Name |Type |Description | 221 | |--- |--- |--- | 222 | |*channelSid*|String|The sid of the channel 223 | |*member*|Object|The member who started typing 224 | 225 | #### `onUserInfoUpdated({updated, userInfo})` 226 | |Name |Type |Description | 227 | |--- |--- |--- | 228 | |*updated*|Constants.TCHUserInfoUpdated|The type of userInfo update 229 | |*userInfo*|UserInfo|The new UserInfo instance 230 | -------------------------------------------------------------------------------- /Example/GiftedMessengerContainer.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | } from 'react'; 4 | 5 | import { 6 | Linking, 7 | Platform, 8 | Dimensions, 9 | Navigator, 10 | } from 'react-native'; 11 | 12 | 13 | import GiftedMessenger from 'react-native-gifted-messenger'; 14 | 15 | import { 16 | Client, 17 | Constants, 18 | AccessManager, 19 | } from 'react-native-twilio-chat'; 20 | 21 | const BOT_GREETINGS = [ 22 | "Hello!", 23 | "Good day, mate!", 24 | "Why hello there!", 25 | "Hola!", 26 | "Bonjour", 27 | "I'm booting up...", 28 | "Hiya!", 29 | "Heellllooooooooooooooo", 30 | ]; 31 | 32 | const BOT_QUESTIONS = [ 33 | "Let's be friends! What can I call you?", 34 | "I'm your new pal! What's your name?", 35 | "I am here to serve, how shall I address you?", 36 | "You're my new friend! What is your preferred name?", 37 | "Let me update my records... what is your name?", 38 | "I'll help you get things started. What is your name?", 39 | ]; 40 | 41 | const BOT_CONFIRMATIONS = [ 42 | "You are now cleared to chat!", 43 | "Feel free to chat with the group", 44 | "I have now connected you to the channel", 45 | "Chat away!", 46 | "Feel free to start chatting", 47 | "The group awaits your input", 48 | "Go for the gold!", 49 | ]; 50 | 51 | let STATUS_BAR_HEIGHT = Navigator.NavigationBar.Styles.General.StatusBarHeight; 52 | 53 | if (Platform.OS === 'android') { 54 | // const ExtraDimensions = require('react-native-extra-dimensions-android'); 55 | STATUS_BAR_HEIGHT = 50; 56 | } 57 | 58 | class GiftedMessengerContainer extends Component { 59 | 60 | constructor(props) { 61 | super(props); 62 | 63 | this._isMounted = false; 64 | this._messages = this.getInitialMessages(); 65 | 66 | this.state = { 67 | messages: this._messages, 68 | isLoadingEarlierMessages: false, 69 | typingMessage: '', 70 | allLoaded: false, 71 | }; 72 | } 73 | 74 | getToken(identity) { 75 | return fetch('http://localhost:3000/token?device=' + Platform.OS + '&identity=' + identity, { 76 | method: 'get', 77 | }) 78 | .then(res => res.json()); 79 | } 80 | 81 | parseMessage(message) { 82 | return { 83 | uniqueId: message.sid, 84 | text: message.body, 85 | name: message.author, 86 | position: message.author === this.state.client.userInfo.identity ? 'right' : 'left', 87 | date: message.timestamp, 88 | }; 89 | } 90 | 91 | initializeMessenging(identity) { 92 | console.log('starting init'); 93 | this.getToken(identity) 94 | .then(({ token }) => { 95 | // initaite new Access Manager 96 | const accessManager = new AccessManager(token); 97 | 98 | accessManager.onTokenWillExpire = () => { 99 | this.getToken(identity) 100 | .then(newToken => accessManager.updateToken(newToken.token)); 101 | }; 102 | 103 | accessManager.onTokenInvalid = () => { 104 | console.log('Token is invalid'); 105 | }; 106 | 107 | accessManager.onTokenExpired = () => { 108 | console.log('Token is expired'); 109 | }; 110 | 111 | // initiate the client with the token, not accessManager 112 | const client = new Client(token); 113 | 114 | client.onError = ({ error, userInfo }) => { 115 | console.log(error); 116 | console.log(userInfo); 117 | }; 118 | 119 | client.onSynchronizationStatusChanged = (status) => { 120 | console.log(status); 121 | }; 122 | 123 | client.onClientConnectionStateChanged = (state) => { 124 | console.log(state); 125 | }; 126 | 127 | client.onClientSynchronized = () => { 128 | console.log('client synced'); 129 | client.getPublicChannels() 130 | .then(res => console.log(res)); 131 | 132 | client.getChannel('general') 133 | .then((channel) => { 134 | channel.initialize() 135 | .then(() => { 136 | console.log(channel); 137 | if (channel.status !== Constants.TCHChannelStatus.Joined) { 138 | channel.join(); 139 | } 140 | }) 141 | .catch((error) => { 142 | console.log(error); 143 | }); 144 | 145 | channel.onTypingStarted = (member) => { 146 | this.setState({ typingMessage: member.userInfo.identity + ' is typing...' }); 147 | }; 148 | 149 | channel.onTypingEnded = () => { 150 | this.setState({ typingMessage: '' }); 151 | }; 152 | 153 | channel.onMessageAdded = message => this.handleReceive(this.parseMessage(message)); 154 | 155 | this.setState({ client, channel, accessManager }); 156 | }); 157 | }; 158 | 159 | client.initialize() 160 | .then(() => { 161 | console.log(client); 162 | console.log('client initilized'); 163 | // register the client with the accessManager 164 | accessManager.registerClient(); 165 | }); 166 | }); 167 | } 168 | 169 | botMessage(message, time = 1000) { 170 | this.setState({ typingMessage: 'MessagingBot is typing...' }); 171 | return new Promise((resolve) => { 172 | setTimeout(() => { 173 | this.setState({ typingMessage: '' }); 174 | this.handleReceive({ 175 | text: message, 176 | uniqueId: Math.round(Math.random() * 10000), 177 | name: 'MessagingBot', 178 | position: 'left', 179 | internal: true 180 | }); 181 | resolve(); 182 | }, time); 183 | }); 184 | } 185 | 186 | componentDidMount() { 187 | // setTimeout(() => { 188 | // this.botMessage(BOT_GREETINGS[Math.floor(Math.random() * BOT_GREETINGS.length)]) 189 | // .then(() => this.botMessage(BOT_QUESTIONS[Math.floor(Math.random() * BOT_QUESTIONS.length)], 2000)); 190 | // }, 500); 191 | this.initializeMessenging('Brad'); 192 | } 193 | 194 | componentWillUnmount() { 195 | this._isMounted = false; 196 | } 197 | 198 | getInitialMessages() { 199 | return []; 200 | } 201 | 202 | setMessageStatus(uniqueId, status) { 203 | const messages = []; 204 | let found = false; 205 | 206 | for (let i = 0; i < this._messages.length; i++) { 207 | if (this._messages[i].uniqueId === uniqueId) { 208 | const clone = Object.assign({}, this._messages[i]); 209 | clone.status = status; 210 | messages.push(clone); 211 | found = true; 212 | } else { 213 | messages.push(this._messages[i]); 214 | } 215 | } 216 | 217 | if (found === true) { 218 | this.setMessages(messages); 219 | } 220 | } 221 | 222 | setMessages(messages) { 223 | this._messages = messages; 224 | 225 | // append the message 226 | this.setState({ 227 | messages, 228 | }); 229 | } 230 | 231 | handleSend(message = {}) { 232 | if (this.state.client) { 233 | this.state.channel.sendMessage(message.text) 234 | .catch(error => console.error(error)); 235 | } else { 236 | this.initializeMessenging(message.text); 237 | message.uniqueId = Math.round(Math.random() * 10000); // simulating server-side unique id generation 238 | this.setMessages(this._messages.concat(message)); 239 | this.botMessage(`Hello ${message.text}!`, 1000) 240 | .then(() => this.botMessage(BOT_CONFIRMATIONS[Math.floor(Math.random() * BOT_CONFIRMATIONS.length)], 2000)); 241 | } 242 | } 243 | 244 | handleReceive(message = {}) { 245 | // make sure that your message contains : 246 | // text, name, image, position: 'left', date, uniqueId 247 | this.setMessages(this._messages.concat(message)); 248 | } 249 | 250 | onErrorButtonPress(message = {}) { 251 | // Your logic here 252 | // re-send the failed message 253 | 254 | // remove the status 255 | this.setMessageStatus(message.uniqueId, ''); 256 | } 257 | 258 | render() { 259 | return ( 260 | this._GiftedMessenger = c} 262 | 263 | styles={{ 264 | bubbleRight: { 265 | marginLeft: 70, 266 | backgroundColor: '#007aff', 267 | }, 268 | }} 269 | 270 | autoFocus={false} 271 | messages={this.state.messages} 272 | handleSend={this.handleSend.bind(this)} 273 | onErrorButtonPress={this.onErrorButtonPress.bind(this)} 274 | maxHeight={Dimensions.get('window').height - Navigator.NavigationBar.Styles.General.NavBarHeight - STATUS_BAR_HEIGHT} 275 | 276 | loadEarlierMessagesButton={false} 277 | 278 | senderName='Awesome Developer' 279 | senderImage={null} 280 | onImagePress={this.onImagePress} 281 | displayNames={true} 282 | 283 | onChangeText={() => this.state.channel ? this.state.channel.typing() : false} 284 | 285 | parseText={true} // enable handlePhonePress, handleUrlPress and handleEmailPress 286 | handlePhonePress={this.handlePhonePress} 287 | handleUrlPress={this.handleUrlPress} 288 | handleEmailPress={this.handleEmailPress} 289 | 290 | isLoadingEarlierMessages={this.state.isLoadingEarlierMessages} 291 | 292 | typingMessage={this.state.typingMessage} 293 | /> 294 | ); 295 | } 296 | 297 | handleUrlPress(url) { 298 | Linking.openURL(url); 299 | } 300 | 301 | } 302 | 303 | 304 | module.exports = GiftedMessengerContainer; 305 | -------------------------------------------------------------------------------- /lib/Channel.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | NativeAppEventEmitter, 4 | Platform, 5 | } from 'react-native'; 6 | 7 | import Message from './Message'; 8 | import Member from './Member'; 9 | import UserInfo from './UserInfo'; 10 | import Paginator from './Paginator'; 11 | 12 | const { 13 | TwilioChatChannels, 14 | TwilioChatMessages, 15 | TwilioChatMembers, 16 | } = NativeModules; 17 | 18 | class Channel { 19 | constructor(props) { 20 | this.sid = props.sid; 21 | this.friendlyName = props.friendlyName; 22 | this.uniqueName = props.uniqueName; 23 | if (props.synchronizationStatus) this.synchronizationStatus = props.synchronizationStatus; 24 | if (props.status) this.status = props.status; 25 | if (props.type) this.type = props.type; 26 | this.attributes = props.attributes; 27 | this.dateCreated = new Date(props.dateCreated); 28 | this.dateUpdated = new Date(props.dateUpdated); 29 | this.createdBy = props.createdBy; 30 | if (props.membersCount) this.membersCount = props.membersCount; 31 | if (props.messagesCount) this.messageCount = props.messagesCount; 32 | 33 | this.onSynchronizationStatusChanged = null; 34 | this.onChanged = null; 35 | this.onDeleted = null; 36 | this.onMemberJoined = null; 37 | this.onMemberChanged = null; 38 | this.onMemberLeft = null; 39 | this.onMemberUserInfoUpdated = null; 40 | this.onMessageAdded = null; 41 | this.onMessageChanged = null; 42 | this.onMessageDeleted = null; 43 | this.onTypingStarted = null; 44 | this.onTypingEnded = null; 45 | this.onToastReceived = null; 46 | 47 | // event handlers 48 | this._channelSynchronizationStatusChangedSubscription = NativeAppEventEmitter.addListener( 49 | 'chatClient:channel:synchronizationStatusChanged', 50 | ({ channelSid, status }) => { 51 | if (channelSid === this.sid && this.onSynchronizationStatusChanged) this.onSynchronizationStatusChanged(status); 52 | }, 53 | ); 54 | 55 | this._channelChangedSubscription = NativeAppEventEmitter.addListener( 56 | 'chatClient:channelChanged', 57 | (channel) => { 58 | if (channel.sid === this.sid && this.onChanged) { 59 | this.sid = channel.sid; 60 | this.friendlyName = channel.friendlyName; 61 | this.uniqueName = channel.uniqueName; 62 | this.synchronizationStatus = channel.synchronizationStatus; 63 | this.status = channel.status; 64 | this.type = channel.type; 65 | this.attributes = channel.attributes; 66 | this.dateCreated = new Date(channel.dateCreated); 67 | this.dateUpdated = new Date(channel.dateUpdated); 68 | this.createdBy = props.createdBy; 69 | this.onChanged(); 70 | } 71 | }, 72 | ); 73 | 74 | this._channelDeletedSubscription = NativeAppEventEmitter.addListener( 75 | 'chatClient:channelDeleted', 76 | (channel) => { 77 | if (channel.sid === this.sid && this.onDeleted) this.onDeleted(); 78 | }, 79 | ); 80 | 81 | this._channelMemberJoinedSubscription = NativeAppEventEmitter.addListener( 82 | 'chatClient:channel:memberJoined', 83 | ({ channelSid, member }) => { 84 | if (channelSid === this.sid && this.onMemberJoined) this.onMemberJoined(new Member(member)); 85 | }, 86 | ); 87 | 88 | this._channelMemberChangedSubscription = NativeAppEventEmitter.addListener( 89 | 'chatClient:channel:memberChanged', 90 | ({ channelSid, member }) => { 91 | if (channelSid === this.sid && this.onMemberChanged) this.onMemberChanged(new Member(member)); 92 | }, 93 | ); 94 | 95 | this._channelMemberLeftSubscription = NativeAppEventEmitter.addListener( 96 | 'chatClient:channel:memberLeft', 97 | ({ channelSid, member }) => { 98 | if (channelSid === this.sid && this.onMemberLeft) this.onMemberLeft(new Member(member)); 99 | }, 100 | ); 101 | 102 | this._channelMessageAddedSubscription = NativeAppEventEmitter.addListener( 103 | 'chatClient:channel:messageAdded', 104 | ({ channelSid, message }) => { 105 | if (channelSid === this.sid && this.onMessageAdded) this.onMessageAdded(new Message(message, channelSid)); 106 | }, 107 | ); 108 | 109 | this._channelMessageChangedSubscription = NativeAppEventEmitter.addListener( 110 | 'chatClient:channel:messageChanged', 111 | ({ channelSid, message }) => { 112 | if (channelSid === this.sid && this.onMessageChanged) this.onMessageChanged(new Message(message, channelSid)); 113 | }, 114 | ); 115 | 116 | this._channelMessageDeletedSubscription = NativeAppEventEmitter.addListener( 117 | 'chatClient:channel:messageDeleted', 118 | ({ channelSid, message }) => { 119 | if (channelSid === this.sid && this.onMessageDeleted) this.onMessageDeleted(new Message(message, channelSid)); 120 | }, 121 | ); 122 | 123 | this._typingChannelStartedSubscription = NativeAppEventEmitter.addListener( 124 | 'chatClient:typingStartedOnChannel', 125 | ({ channelSid, member }) => { 126 | if (channelSid === this.sid && this.onTypingStarted) this.onTypingStarted(new Member(member)); 127 | }, 128 | ); 129 | 130 | this._typingChannelEndedSubscription = NativeAppEventEmitter.addListener( 131 | 'chatClient:typingEndedOnChannel', 132 | ({ channelSid, member }) => { 133 | if (channelSid === this.sid && this.onTypingEnded) this.onTypingEnded(new Member(member)); 134 | }, 135 | ); 136 | 137 | this._toastReceivedChannelSubscription = NativeAppEventEmitter.addListener( 138 | 'chatClient:toastReceivedOnChannel', 139 | ({ channelSid, message }) => { 140 | if (channelSid === this.sid && this.onToastReceived) this.onToastReceived(new Message(message)); 141 | }, 142 | ); 143 | 144 | this._memberUserInfoUpdatedSubscription = NativeAppEventEmitter.addListener( 145 | 'chatClient:channel:member:userInfoUpdated', 146 | ({ channelSid, updated, userInfo }) => { 147 | if (channelSid === this.sid && this.onMemberUserInfoUpdated) this.onMemberUserInfoUpdated({ updated, userInfo: new UserInfo(userInfo) }); 148 | }, 149 | ); 150 | } 151 | 152 | initialize() { 153 | return TwilioChatChannels.synchronize(this.sid); 154 | } 155 | 156 | setAttributes(attributes) { 157 | this.attributes = attributes; 158 | return TwilioChatChannels.setAttributes(this.sid, attributes); 159 | } 160 | 161 | setFriendlyName(friendlyName) { 162 | this.friendlyName = friendlyName; 163 | return TwilioChatChannels.setFriendlyName(this.sid, friendlyName); 164 | } 165 | 166 | setUniqueName(uniqueName) { 167 | this.uniqueName = uniqueName; 168 | return TwilioChatChannels.setUniqueName(this.sid, uniqueName); 169 | } 170 | 171 | join() { 172 | return TwilioChatChannels.join(this.sid); 173 | } 174 | 175 | declineInvitation() { 176 | return TwilioChatChannels.declineInvitation(this.sid); 177 | } 178 | 179 | leave() { 180 | return TwilioChatChannels.leave(this.sid); 181 | } 182 | 183 | destroy() { 184 | return TwilioChatChannels.destroy(this.sid); 185 | } 186 | 187 | typing() { 188 | TwilioChatChannels.typing(this.sid); 189 | } 190 | 191 | getMember(identity) { 192 | return TwilioChatChannels.getMember(this.sid, identity) 193 | .then(member => new Member(member)); 194 | } 195 | 196 | getMembers() { 197 | return TwilioChatMembers.getMembers(this.sid) 198 | .then(({ sid, type, paginator }) => new Paginator(sid, type, paginator)); 199 | } 200 | 201 | add(identity) { 202 | return TwilioChatMembers.add(this.sid, identity); 203 | } 204 | 205 | invite(identity) { 206 | return TwilioChatMembers.invite(this.sid, identity); 207 | } 208 | 209 | remove(identity) { 210 | return TwilioChatMembers.remove(this.sid, identity); 211 | } 212 | 213 | getLastConsumedMessageIndex() { 214 | return TwilioChatMessages.getLastConsumedMessageIndex(this.sid); 215 | } 216 | 217 | sendMessage(body, attributes = null) { 218 | return TwilioChatMessages.sendMessage(this.sid, body, attributes); 219 | } 220 | 221 | removeMessage(index) { 222 | return TwilioChatMessages.removeMessage(this.sid, index); 223 | } 224 | 225 | getMessages(count = 10) { 226 | return TwilioChatMessages.getLastMessages(this.sid, count) 227 | .then(messages => messages.map(message => new Message(message, this.sid))); 228 | } 229 | 230 | getMessagesBefore(index, count) { 231 | return TwilioChatMessages.getMessagesBefore(this.sid, index, count) 232 | .then(messages => messages.map(message => new Message(message, this.sid))); 233 | } 234 | 235 | getMessagesAfter(index, count) { 236 | return TwilioChatMessages.getMessagesAfter(this.sid, index, count) 237 | .then(messages => messages.map(message => new Message(message, this.sid))); 238 | } 239 | 240 | getMessage(index) { 241 | return TwilioChatMessages.getMessage(this.sid, index) 242 | .then(message => new Message(message, this.sid)); 243 | } 244 | 245 | getMessageForConsumption(index) { 246 | if (Platform.OS !== 'ios') console.warn('getMessageForConsumption is only available on iOS.'); 247 | else { 248 | return TwilioChatMessages.messageForConsumptionIndex(this.sid, index) 249 | .then(message => new Message(message, this.sid)); 250 | } 251 | } 252 | 253 | setLastConsumedMessageIndex(index) { 254 | TwilioChatMessages.setLastConsumedMessageIndex(this.sid, index); 255 | } 256 | 257 | advanceLastConsumedMessageIndex(index) { 258 | TwilioChatMessages.advanceLastConsumedMessageIndex(this.sid, index); 259 | } 260 | 261 | setAllMessagesConsumed() { 262 | TwilioChatMessages.setAllMessagesConsumed(this.sid); 263 | } 264 | 265 | getUnconsumedMessagesCount() { 266 | return TwilioChatChannels.getUnconsumedMessagesCount(this.sid); 267 | } 268 | 269 | getMessagesCount() { 270 | return TwilioChatChannels.getMessagesCount(this.sid); 271 | } 272 | 273 | getMembersCount() { 274 | return TwilioChatChannels.getMembersCount(this.sid); 275 | } 276 | 277 | close() { 278 | this._channelChangedSubscription.remove(); 279 | this._channelDeletedSubscription.remove(); 280 | this._channelSynchronizationStatusChangedSubscription.remove(); 281 | this._channelMemberJoinedSubscription.remove(); 282 | this._channelMemberChangedSubscription.remove(); 283 | this._channelMemberLeftSubscription.remove(); 284 | this._channelMessageAddedSubscription.remove(); 285 | this._channelMessageChangedSubscription.remove(); 286 | this._channelMessageDeletedSubscription.remove(); 287 | this._typingChannelStartedSubscription.remove(); 288 | this._typingChannelEndedSubscription.remove(); 289 | this._toastReceivedChannelSubscription.remove(); 290 | this._memberUserInfoUpdatedSubscription.remove(); 291 | } 292 | } 293 | 294 | export default Channel; 295 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTConvert+TwilioChatClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTConvert+TwilioChatClient.m 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 5/31/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTConvert+TwilioChatClient.h" 10 | #import 11 | 12 | @implementation RCTConvert (TwilioChatClient) 13 | 14 | RCT_ENUM_CONVERTER(TCHClientSynchronizationStatus,(@{ 15 | @"Started" : @(TCHClientSynchronizationStatusStarted), 16 | @"ChannelsListCompleted" : @(TCHClientSynchronizationStatusChannelsListCompleted), 17 | @"Completed" : @(TCHClientSynchronizationStatusCompleted), 18 | @"Failed" : @(TCHClientSynchronizationStatusFailed), 19 | }), TCHClientSynchronizationStatusStarted, integerValue) 20 | 21 | 22 | RCT_ENUM_CONVERTER(TCHChannelSynchronizationStatus,(@{ 23 | @"None" : @(TCHChannelSynchronizationStatusNone), 24 | @"Identifier" : @(TCHChannelSynchronizationStatusIdentifier), 25 | @"Metadata" : @(TCHChannelSynchronizationStatusMetadata), 26 | @"All" : @(TCHChannelSynchronizationStatusAll), 27 | @"Failed" : @(TCHChannelSynchronizationStatusFailed), 28 | }), TCHChannelSynchronizationStatusNone, integerValue) 29 | 30 | RCT_ENUM_CONVERTER(TCHChannelStatus,(@{ 31 | @"Invited" : @(TCHChannelStatusInvited), 32 | @"Joined" : @(TCHChannelStatusJoined), 33 | @"NotParticipating" : @(TCHChannelStatusNotParticipating), 34 | }), TCHChannelStatusInvited, integerValue) 35 | 36 | RCT_ENUM_CONVERTER(TCHChannelType,(@{ 37 | @"Public" : @(TCHChannelTypePublic), 38 | @"Private" : @(TCHChannelTypePrivate), 39 | }), TCHChannelTypePublic, integerValue) 40 | 41 | RCT_ENUM_CONVERTER(TCHUserInfoUpdate,(@{ 42 | @"FriendlyName" : @(TCHUserInfoUpdateFriendlyName), 43 | @"Attributes" : @(TCHUserInfoUpdateAttributes), 44 | @"ReachabilityOnline": @(TCHUserInfoUpdateReachabilityOnline), 45 | @"ReachabilityNotifiable": @(TCHUserInfoUpdateReachabilityNotifiable), 46 | }), TCHUserInfoUpdateFriendlyName, integerValue) 47 | 48 | RCT_ENUM_CONVERTER(TCHClientSynchronizationStrategy,(@{ 49 | @"All" : @(TCHClientSynchronizationStrategyAll), 50 | @"ChannelsList" : @(TCHClientSynchronizationStrategyChannelsList), 51 | }), TCHClientSynchronizationStrategyAll, integerValue) 52 | 53 | RCT_ENUM_CONVERTER(TCHLogLevel,(@{ 54 | @"Fatal" : @(TCHLogLevelFatal), 55 | @"Critical" : @(TCHLogLevelCritical), 56 | @"Warning" : @(TCHLogLevelWarning), 57 | @"Info" : @(TCHLogLevelInfo), 58 | @"Debug" : @(TCHLogLevelDebug), 59 | }), TCHLogLevelFatal, integerValue) 60 | 61 | RCT_ENUM_CONVERTER(TCHClientConnectionState,(@{ 62 | @"Unknown" : @(TCHClientConnectionStateUnknown), 63 | @"Disconnected" : @(TCHClientConnectionStateDisconnected), 64 | @"Connected" : @(TCHClientConnectionStateConnected), 65 | @"Connecting" : @(TCHClientConnectionStateConnecting), 66 | @"Denied" : @(TCHClientConnectionStateDenied), 67 | @"Error" : @(TCHClientConnectionStateError) 68 | }), TCHClientConnectionStateUnknown, integerValue) 69 | 70 | 71 | + (NSDictionary *)TwilioAccessManager:(TwilioAccessManager *)accessManager { 72 | if (!accessManager) { 73 | return RCTNullIfNil(nil); 74 | } 75 | return @{ 76 | @"token": accessManager.currentToken, 77 | @"expirationDate": @(accessManager.expiryTime.timeIntervalSince1970 * 1000) 78 | }; 79 | } 80 | 81 | + (NSDictionary *)TwilioChatClient:(TwilioChatClient *)client { 82 | if (!client) { 83 | return RCTNullIfNil(nil); 84 | } 85 | return @{ 86 | @"userInfo": [self TCHUserInfo:client.userInfo], 87 | @"synchronizationStatus": @(client.synchronizationStatus), 88 | @"version": client.version, 89 | @"isReachabilityEnabled": @(client.isReachabilityEnabled) 90 | }; 91 | } 92 | 93 | 94 | + (NSDictionary *)TCHUserInfo:(TCHUserInfo *)userInfo { 95 | if (!userInfo) { 96 | return RCTNullIfNil(nil); 97 | } 98 | return @{ 99 | @"identity": userInfo.identity, 100 | @"friendlyName": userInfo.friendlyName, 101 | @"attributes": RCTNullIfNil(userInfo.attributes), 102 | @"isOnline": @(userInfo.isOnline), 103 | @"isNotifiable": @(userInfo.isNotifiable) 104 | }; 105 | } 106 | 107 | + (NSDictionary *)TCHMessage:(TCHMessage *)message { 108 | if (!message) { 109 | return RCTNullIfNil(nil); 110 | } 111 | return @{ 112 | @"sid": message.sid, 113 | @"index": message.index, 114 | @"author": message.author, 115 | @"body": message.body, 116 | @"timestamp": message.timestamp, 117 | @"timestampAsDate": @(message.timestampAsDate.timeIntervalSince1970 * 1000), 118 | @"dateUpdated": message.dateUpdated, 119 | @"dateUpdatedDate": @(message.dateUpdatedAsDate.timeIntervalSince1970 * 1000), 120 | @"lastUpdatedBy": message.lastUpdatedBy, 121 | @"attributes": RCTNullIfNil(message.attributes) 122 | }; 123 | } 124 | 125 | + (NSDictionary *)TCHMember:(TCHMember *)member { 126 | if (!member) { 127 | return RCTNullIfNil(nil); 128 | } 129 | return @{ 130 | @"userInfo": [RCTConvert TCHUserInfo:member.userInfo], 131 | @"lastConsumedMessageIndex": RCTNullIfNil(member.lastConsumedMessageIndex), 132 | @"lastConsumptionTimestamp": RCTNullIfNil(member.lastConsumptionTimestamp) 133 | }; 134 | } 135 | 136 | + (NSDictionary *)TCHChannel:(TCHChannel *)channel { 137 | if (!channel) { 138 | return RCTNullIfNil(nil); 139 | } 140 | return @{ 141 | @"sid": channel.sid, 142 | @"friendlyName": channel.friendlyName, 143 | @"uniqueName": channel.uniqueName, 144 | @"status": @(channel.status), 145 | @"type": @(channel.type), 146 | @"attributes": RCTNullIfNil(channel.attributes), 147 | @"synchronizationStatus": @(channel.synchronizationStatus), 148 | @"dateCreated": channel.dateCreated, 149 | @"dateUpdated": channel.dateUpdated, 150 | @"createdBy": channel.createdBy 151 | }; 152 | } 153 | 154 | + (NSDictionary *)TCHChannelDescriptor:(TCHChannelDescriptor *)channel { 155 | if (!channel) { 156 | return RCTNullIfNil(nil); 157 | } 158 | return @{ 159 | @"sid": channel.sid, 160 | @"friendlyName": channel.friendlyName, 161 | @"uniqueName": channel.uniqueName, 162 | @"attributes": RCTNullIfNil(channel.attributes), 163 | @"messageCount": @(channel.messagesCount), 164 | @"membersCount": @(channel.membersCount), 165 | @"dateCreated": @(channel.dateCreated.timeIntervalSince1970 * 1000), 166 | @"dateUpdated": @(channel.dateUpdated.timeIntervalSince1970 * 1000), 167 | @"createdBy": channel.createdBy 168 | }; 169 | } 170 | 171 | + (NSArray *)TCHMembers:(NSArray*)members { 172 | if (!members) { 173 | return RCTNullIfNil(nil); 174 | } 175 | NSMutableArray *response = [NSMutableArray array]; 176 | for (TCHMember *member in members) { 177 | [response addObject:[self TCHMember:member]]; 178 | } 179 | return response; 180 | } 181 | 182 | + (NSArray *)TCHChannels:(NSArray*)channels { 183 | if (!channels) { 184 | return RCTNullIfNil(nil); 185 | } 186 | NSMutableArray *response = [NSMutableArray array]; 187 | for (TCHChannel *channel in channels) { 188 | [response addObject:[self TCHChannel:channel]]; 189 | } 190 | return response; 191 | } 192 | 193 | + (NSArray *)TCHChannelDescriptors:(NSArray*)channels { 194 | if (!channels) { 195 | return RCTNullIfNil(nil); 196 | } 197 | NSMutableArray *response = [NSMutableArray array]; 198 | for (TCHChannelDescriptor *channel in channels) { 199 | [response addObject:[self TCHChannelDescriptor:channel]]; 200 | } 201 | return response; 202 | } 203 | 204 | + (NSArray *)TCHMessages:(NSArray *)messages { 205 | if (!messages) { 206 | return RCTNullIfNil(nil); 207 | } 208 | NSMutableArray *response = [NSMutableArray array]; 209 | for (TCHMessage *message in messages) { 210 | [response addObject:[self TCHMessage:message]]; 211 | } 212 | return response; 213 | } 214 | 215 | + (NSDictionary *)TCHMemberPaginator:(TCHMemberPaginator *)paginator { 216 | if (!paginator) { 217 | return RCTNullIfNil(nil); 218 | } 219 | return @{ 220 | @"hasNextPage": @(paginator.hasNextPage), 221 | @"items": [self TCHMembers:paginator.items] 222 | }; 223 | } 224 | 225 | + (NSDictionary *)TCHChannelPaginator:(TCHChannelPaginator *)paginator { 226 | if (!paginator) { 227 | return RCTNullIfNil(nil); 228 | } 229 | return @{ 230 | @"hasNextPage": @(paginator.hasNextPage), 231 | @"items": [self TCHChannels:paginator.items] 232 | }; 233 | } 234 | 235 | + (NSDictionary *)TCHChannelDescriptorPaginator:(TCHChannelDescriptorPaginator *)paginator { 236 | if (!paginator) { 237 | return RCTNullIfNil(nil); 238 | } 239 | return @{ 240 | @"hasNextPage": @(paginator.hasNextPage), 241 | @"items": [self TCHChannelDescriptors:paginator.items] 242 | }; 243 | } 244 | 245 | + (NSData *)dataWithHexString:(NSString *)hex { 246 | // Source: https://opensource.apple.com/source/Security/Security-55471.14.18/libsecurity_transform/NSData+HexString.m 247 | char buf[3]; 248 | buf[2] = '\0'; 249 | NSAssert(0 == [hex length] % 2, @"Hex strings should have an even number of digits (%@)", hex); 250 | unsigned char *bytes = malloc([hex length]/2); 251 | unsigned char *bp = bytes; 252 | for (CFIndex i = 0; i < [hex length]; i += 2) { 253 | buf[0] = [hex characterAtIndex:i]; 254 | buf[1] = [hex characterAtIndex:i+1]; 255 | char *b2 = NULL; 256 | *bp++ = strtol(buf, &b2, 16); 257 | NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %d)", hex, i); 258 | } 259 | 260 | return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES]; 261 | } 262 | 263 | 264 | @end 265 | -------------------------------------------------------------------------------- /lib/Client.js: -------------------------------------------------------------------------------- 1 | import { 2 | NativeModules, 3 | NativeAppEventEmitter, 4 | Platform, 5 | } from 'react-native'; 6 | 7 | import Channel from './Channel'; 8 | import Member from './Member'; 9 | import Message from './Message'; 10 | import UserInfo from './UserInfo'; 11 | import Constants from './Constants'; 12 | 13 | import Paginator from './Paginator'; 14 | 15 | const { 16 | TwilioChatClient, 17 | TwilioChatChannels, 18 | } = NativeModules; 19 | 20 | class Client { 21 | constructor(initialToken, synchronizationStrategy, initialMessageCount) { 22 | // properties 23 | this.initialToken = initialToken; 24 | this.userInfo = {}; 25 | this.isReachabilityEnabled = false; 26 | 27 | // initial event handlers 28 | this.onClientConnectionStateChanged = null; 29 | this.onSynchronizationStatusChanged = null; 30 | this.onChannelAdded = null; 31 | this.onChannelChanged = null; 32 | this.onChannelDeleted = null; 33 | this.onChannelInvited = null; 34 | this.onChannelSynchronizationStatusChanged = null; 35 | this.onMemberJoined = null; 36 | this.onMemberChanged = null; 37 | this.onMemberLeft = null; 38 | this.onMemberUserInfoUpdated = null; 39 | this.onMessageAdded = null; 40 | this.onMessageChanged = null; 41 | this.onMessageDeleted = null; 42 | this.onError = null; 43 | this.onTypingStarted = null; 44 | this.onTypingEnded = null; 45 | 46 | this.onToastSubscribed = null; 47 | this.onToastReceived = null; 48 | this.onToastFailed = null; 49 | 50 | this.onClientSynchronized = null; 51 | if (!synchronizationStrategy) { 52 | synchronizationStrategy = Constants.TCHClientSynchronizationStrategy.ChannelsList; 53 | } 54 | this._properties = { 55 | synchronizationStrategy, 56 | initialMessageCount: initialMessageCount ? initialMessageCount : 100, 57 | }; 58 | 59 | // event handlers 60 | this._clientConnectionStateChangedSubscription = NativeAppEventEmitter.addListener( 61 | 'chatClient:connectionStateChanged', 62 | (state) => { 63 | if (this.onClientConnectionStateChanged) this.onClientConnectionStateChanged(state); 64 | }, 65 | ); 66 | 67 | 68 | this._synchronizationStatusChangedSubscription = NativeAppEventEmitter.addListener( 69 | 'chatClient:synchronizationStatusChanged', 70 | (status) => { 71 | this.synchronizationStatus = status; 72 | if (status === Constants.TCHClientSynchronizationStatus.Completed) { 73 | TwilioChatClient.userInfo() 74 | .then((userInfo) => { this.userInfo = new UserInfo(userInfo); }) 75 | .then(() => { 76 | if (this.onClientSynchronized) this.onClientSynchronized(); 77 | }); 78 | } 79 | if (this.onSynchronizationStatusChanged) this.onSynchronizationStatusChanged(status); 80 | }, 81 | ); 82 | 83 | this._channelAddedSubscription = NativeAppEventEmitter.addListener( 84 | 'chatClient:channelAdded', 85 | (channel) => { 86 | if (this.onChannelAdded) this.onChannelAdded(new Channel(channel)); 87 | }, 88 | ); 89 | 90 | this._channelChangedSubscription = NativeAppEventEmitter.addListener( 91 | 'chatClient:channelChanged', 92 | (channel) => { 93 | if (this.onChannelChanged) this.onChannelChanged(new Channel(channel)); 94 | }, 95 | ); 96 | 97 | this._channelInvitedSubscription = NativeAppEventEmitter.addListener( 98 | 'chatClient:channelInvited', 99 | (channel) => { 100 | if (this.onChannelInvited) this.onChannelInvited(new Channel(channel)); 101 | }, 102 | ); 103 | 104 | 105 | this._channelDeletedSubscription = NativeAppEventEmitter.addListener( 106 | 'chatClient:channelDeleted', 107 | (channel) => { 108 | if (this.onChannelDeleted) this.onChannelDeleted(new Channel(channel)); 109 | }, 110 | ); 111 | 112 | this._channelSynchronizationStatusChangedSubscription = NativeAppEventEmitter.addListener( 113 | 'chatClient:channel:synchronizationStatusChanged', 114 | ({ channelSid, status }) => { 115 | if (this.onChannelSynchronizationStatusChanged) { 116 | this.onChannelSynchronizationStatusChanged({ 117 | channelSid, 118 | status, 119 | }); 120 | } 121 | }, 122 | ); 123 | 124 | this._channelMemberJoinedSubscription = NativeAppEventEmitter.addListener( 125 | 'chatClient:channel:memberJoined', 126 | ({ channelSid, member }) => { 127 | if (this.onMemberJoined) this.onMemberJoined({ channelSid, member: new Member(member) }); 128 | }, 129 | ); 130 | 131 | this._channelMemberChangedSubscription = NativeAppEventEmitter.addListener( 132 | 'chatClient:channel:memberChanged', 133 | ({ channelSid, member }) => { 134 | if (this.onMemberChanged) this.onMemberChanged({channelSid, member: new Member(member) }); 135 | }, 136 | ); 137 | 138 | this._channelMemberLeftSubscription = NativeAppEventEmitter.addListener( 139 | 'chatClient:channel:memberLeft', 140 | ({ channelSid, member }) => { 141 | if (this.onMemberLeft) this.onMemberLeft({ channelSid, member: new Member(member) }); 142 | }, 143 | ); 144 | 145 | this._channelMessageAddedSubscription = NativeAppEventEmitter.addListener( 146 | 'chatClient:channel:messageAdded', 147 | ({ channelSid, message }) => { 148 | if (this.onMessageAdded) { 149 | this.onMessageAdded({ channelSid, message: new Message(message, channelSid) }); 150 | } 151 | }, 152 | ); 153 | 154 | this._channelMessageChangedSubscription = NativeAppEventEmitter.addListener( 155 | 'chatClient:channel:messageChanged', 156 | ({ channelSid, message }) => { 157 | if (this.onMessageChanged) { 158 | this.onMessageChanged({ 159 | channelSid, 160 | message: new Message(message, channelSid), 161 | }); 162 | } 163 | }, 164 | ); 165 | 166 | this._channelMessageDeletedSubscription = NativeAppEventEmitter.addListener( 167 | 'chatClient:channel:messageDeleted', 168 | ({ channelSid, message }) => { 169 | if (this.onMessageDeleted) { 170 | this.onMessageDeleted({ 171 | channelSid, 172 | message: new Message(message, channelSid), 173 | }); 174 | } 175 | }, 176 | ); 177 | 178 | this._ErrorReceivedSubscription = NativeAppEventEmitter.addListener( 179 | 'chatClient:errorReceived', 180 | ({ error, userInfo }) => { 181 | if (this.onError) this.onError({ error, userInfo }); 182 | }, 183 | ); 184 | 185 | this._typingChannelStartedSubscription = NativeAppEventEmitter.addListener( 186 | 'chatClient:typingStartedOnChannel', 187 | ({ channelSid, member }) => { 188 | if (this.onTypingStarted) this.onTypingStarted({ channelSid, member: new Member(member) }); 189 | }, 190 | ); 191 | 192 | this._typingChannelEndedSubscription = NativeAppEventEmitter.addListener( 193 | 'chatClient:typingEndedOnChannel', 194 | ({ channelSid, member }) => { 195 | if (this.onTypingEnded) this.onTypingEnded({ channelSid, member: new Member(member) }); 196 | }, 197 | ); 198 | 199 | this._toastSubscribedSubscription = NativeAppEventEmitter.addListener( 200 | 'chatClient:toastSubscribed', 201 | () => { 202 | if (this.onToastSubscribed) this.onToastSubscribed(); 203 | }, 204 | ); 205 | 206 | this._toastReceivedChannelSubscription = NativeAppEventEmitter.addListener( 207 | 'chatClient:toastReceived', 208 | ({ channelSid, messageSid }) => { 209 | if (this.onToastReceived) this.onToastReceived({ channelSid, messageSid }); 210 | }, 211 | ); 212 | 213 | this._toastRegistrationFailedSubscription = NativeAppEventEmitter.addListener( 214 | 'chatClient:toastFailed', 215 | ({ error, userInfo }) => { 216 | if (this.onToastFailed) this.onToastFailed({ error, userInfo }); 217 | }, 218 | ); 219 | 220 | this._userInfoUpdatedSubscription = NativeAppEventEmitter.addListener( 221 | 'chatClient:userInfoUpdated', 222 | ({ updated, userInfo }) => { 223 | this.userInfo = new UserInfo(userInfo); 224 | if (this.onUserInfoUpdated) this.onUserInfoUpdated({ updated, userInfo: this.userInfo }); 225 | }, 226 | ); 227 | 228 | this._channelMemberUserInfoUpdatedSubscription = NativeAppEventEmitter.addListener( 229 | 'chatClient:channel:member:userInfoUpdated', 230 | ({ channelSid, updated, userInfo }) => { 231 | if (this.onMemberUserInfoUpdated) { 232 | this.onMemberUserInfoUpdated({ channelSid, updated, userInfo: new UserInfo(userInfo) }); 233 | } 234 | }, 235 | ); 236 | } 237 | 238 | initialize() { 239 | return TwilioChatClient.createClient(this.initialToken, this._properties) 240 | .then((client) => { 241 | if (client) { 242 | this.version = client.version; 243 | this.synchronizationStatus = client.synchronizationStatus; 244 | this.isReachabilityEnabled = client.isReachabilityEnabled; 245 | } 246 | return true; 247 | }); 248 | } 249 | 250 | getUserChannels() { 251 | return TwilioChatChannels.getUserChannels() 252 | .then(({ sid, type, paginator }) => new Paginator(sid, type, paginator)); 253 | } 254 | 255 | getPublicChannels() { 256 | return TwilioChatChannels.getPublicChannels() 257 | .then(({ sid, type, paginator }) => new Paginator(sid, type, paginator)); 258 | } 259 | 260 | getChannel(sidOrUniqueName) { 261 | return TwilioChatChannels.getChannel(sidOrUniqueName) 262 | .then(channel => new Channel(channel)); 263 | } 264 | 265 | createChannel(options) { 266 | const parsedOptions = {}; 267 | for (const key in options) { 268 | let newKey = null; 269 | switch(key) { 270 | case 'friendlyName': 271 | newKey = Constants.TCHChannelOption.FriendlyName; 272 | break; 273 | case 'uniqueName': 274 | newKey = Constants.TCHChannelOption.UniqueName; 275 | break; 276 | case 'type': 277 | newKey = Constants.TCHChannelOption.Type; 278 | break; 279 | case 'attributes': 280 | newKey = Constants.TCHChannelOption.Attributes; 281 | break; 282 | } 283 | parsedOptions[newKey] = options[key]; 284 | } 285 | return TwilioChatChannels.createChannel(parsedOptions) 286 | .then(channel => new Channel(channel)); 287 | } 288 | 289 | setLogLevel(logLevel) { 290 | TwilioChatClient.setLogLevel(logLevel); 291 | } 292 | 293 | register(token) { 294 | TwilioChatClient.register(token); 295 | } 296 | 297 | unregister(token) { 298 | TwilioChatClient.unregister(token); 299 | } 300 | 301 | handleNotification(notification) { 302 | TwilioChatClient.handleNotification(notification); 303 | } 304 | 305 | shutdown() { 306 | TwilioChatClient.shutdown(); 307 | if (Platform.OS === 'android') { 308 | TwilioChatChannels.shutdown(); 309 | } 310 | this._removeListeners(); 311 | } 312 | 313 | _removeListeners() { 314 | this._clientConnectionStateChangedSubscription.remove() 315 | this._synchronizationStatusChangedSubscription.remove(); 316 | this._channelAddedSubscription.remove(); 317 | this._channelChangedSubscription.remove(); 318 | this._channelDeletedSubscription.remove(); 319 | this._channelInvitedSubscription.remove(); 320 | this._channelSynchronizationStatusChangedSubscription.remove(); 321 | this._channelMemberJoinedSubscription.remove(); 322 | this._channelMemberChangedSubscription.remove(); 323 | this._channelMemberLeftSubscription.remove(); 324 | this._channelMessageAddedSubscription.remove(); 325 | this._channelMessageChangedSubscription.remove(); 326 | this._channelMessageDeletedSubscription.remove(); 327 | this._ErrorReceivedSubscription.remove(); 328 | this._typingChannelStartedSubscription.remove(); 329 | this._typingChannelEndedSubscription.remove(); 330 | this._toastSubscribedSubscription.remove(); 331 | this._toastReceivedChannelSubscription.remove(); 332 | this._toastRegistrationFailedSubscription.remove(); 333 | this._userInfoUpdatedSubscription.remove(); 334 | this._channelMemberUserInfoUpdatedSubscription.remove(); 335 | } 336 | } 337 | 338 | export default Client; 339 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatMessages.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTwilioChatMessages.m 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/3/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTTwilioChatMessages.h" 10 | #import "RCTTwilioChatChannels.h" 11 | #import "RCTTwilioChatClient.h" 12 | #import "RCTConvert+TwilioChatClient.h" 13 | #import 14 | 15 | @implementation RCTTwilioChatMessages 16 | 17 | RCT_EXPORT_MODULE() 18 | 19 | - (void)loadMessagesFromChannelSid:(NSString *)sid :(void (^)(TCHResult *result, TCHMessages *messages))completion { 20 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 21 | completion(result, [channel messages]); 22 | }]; 23 | } 24 | 25 | #pragma mark Messages Methods 26 | 27 | RCT_REMAP_METHOD(getLastConsumedMessageIndex, channelSid:(NSString *)channelSid last_consumed_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 28 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 29 | if (result.isSuccessful) { 30 | resolve(@[RCTNullIfNil(messages.lastConsumedMessageIndex)]); 31 | } 32 | else { 33 | reject(@"let-last-used-consumption-index-error", @"Error occured while attempting to getLastUsedConsumptionIndex.", result.error); 34 | } 35 | }]; 36 | } 37 | 38 | 39 | RCT_REMAP_METHOD(sendMessage, channelSid:(NSString *)channelSid body:(NSString *)body attributes:(NSDictionary *)attributes send_message_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 40 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 41 | if (result.isSuccessful) { 42 | TCHMessage* message = [messages createMessageWithBody:body]; 43 | void (^sendListener)(TCHResult * sendResult) = ^(TCHResult *sendResult) { 44 | if (sendResult.isSuccessful) { 45 | resolve(@[@TRUE]); 46 | } 47 | else { 48 | reject(@"send-message-error", @"Error occured while attempting to send a message.", sendResult.error); 49 | } 50 | }; 51 | if (attributes != nil) { 52 | [message setAttributes:attributes completion:^(TCHResult *setAttrResult) { 53 | if(setAttrResult.isSuccessful) { 54 | [messages sendMessage:message completion:sendListener]; 55 | } else { 56 | reject(@"send-attributed-message-error", @"Error occured while attempting to send attributed message.", setAttrResult.error); 57 | } 58 | }]; 59 | } 60 | else { 61 | [messages sendMessage:message completion:sendListener]; 62 | } 63 | } 64 | else { 65 | reject(@"send-message-error", @"Error occured while attempting to send a message.", result.error); 66 | } 67 | }]; 68 | } 69 | 70 | RCT_REMAP_METHOD(removeMessage, channelSid:(NSString *)channelSid index:(NSNumber *)index remove_message_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 71 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 72 | if (result.isSuccessful) { 73 | [messages messageWithIndex:index completion:^(TCHResult *result, TCHMessage *message) { 74 | if (result.isSuccessful) { 75 | [messages removeMessage:message completion:^(TCHResult *result) { 76 | if (result.isSuccessful) { 77 | resolve(@[@TRUE]); 78 | } 79 | else { 80 | reject(@"remove-message-error", @"Error occured while attempting to delete a message.", result.error); 81 | } 82 | }]; 83 | } 84 | else { 85 | reject(@"remove-message-error", @"Error occured while attempting to delete a message.", result.error); 86 | } 87 | }]; 88 | } 89 | else { 90 | reject(@"remove-message-error", @"Error occured while attempting to delete a message.", result.error); 91 | } 92 | }]; 93 | } 94 | 95 | RCT_REMAP_METHOD(getLastMessages, channelSid:(NSString *)channelSid count:(NSUInteger)count get_message_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 96 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 97 | if (result.isSuccessful) { 98 | [messages getLastMessagesWithCount:count completion:^(TCHResult *result, NSArray *messages) { 99 | if (result.isSuccessful) { 100 | resolve([RCTConvert TCHMessages:messages]); 101 | } 102 | else { 103 | reject(@"get-message-error", @"Error occured while attempting to getLastMessages.", result.error); 104 | } 105 | }]; 106 | } 107 | else { 108 | reject(@"get-message-error", @"Error occured while attempting to getLastMessages.", result.error); 109 | } 110 | }]; 111 | } 112 | 113 | RCT_REMAP_METHOD(getMessagesBefore, channelSid:(NSString *)channelSid index:(NSUInteger)index withCount:(NSUInteger)withCount get_message_before_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 114 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 115 | if (result.isSuccessful) { 116 | [messages getMessagesBefore:index withCount:withCount completion:^(TCHResult *result, NSArray *messages) { 117 | if (result.isSuccessful) { 118 | resolve([RCTConvert TCHMessages:messages]); 119 | } 120 | else { 121 | reject(@"get-message-error", @"Error occured while attempting to getMessagesBefore.", result.error); 122 | } 123 | }]; 124 | } 125 | else { 126 | reject(@"get-message-error", @"Error occured while attempting to getMessagesBefore.", result.error); 127 | } 128 | }]; 129 | } 130 | 131 | RCT_REMAP_METHOD(getMessagesAfter, channelSid:(NSString *)channelSid index:(NSUInteger)index withCount:(NSUInteger)withCount get_message_after_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 132 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 133 | if (result.isSuccessful) { 134 | [messages getMessagesAfter:index withCount:withCount completion:^(TCHResult *result, NSArray *messages) { 135 | if (result.isSuccessful) { 136 | resolve([RCTConvert TCHMessages:messages]); 137 | } 138 | else { 139 | reject(@"get-message-error", @"Error occured while attempting to getMessagesAfter.", result.error); 140 | } 141 | }]; 142 | } 143 | else { 144 | reject(@"get-message-error", @"Error occured while attempting to getMessagesAfter.", result.error); 145 | } 146 | }]; 147 | } 148 | 149 | RCT_REMAP_METHOD(getMessage, channelSid:(NSString *)channelSid index:(NSNumber *)index message_index_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 150 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 151 | if (result.isSuccessful) { 152 | [messages messageWithIndex:index completion:^(TCHResult *result, TCHMessage *message) { 153 | if (result.isSuccessful) { 154 | resolve([RCTConvert TCHMessage:message]); 155 | } 156 | else { 157 | reject(@"get-message-error", @"Error occured while attempting to getMessage.", result.error); 158 | } 159 | }]; 160 | } 161 | else { 162 | reject(@"get-message-error", @"Error occured while attempting to getMessage.", result.error); 163 | } 164 | }]; 165 | } 166 | 167 | RCT_REMAP_METHOD(messageForConsumptionIndex, channelSid:(NSString *)channelSid index:(NSNumber *)index consumption_message_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 168 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 169 | if (result.isSuccessful) { 170 | [messages messageForConsumptionIndex:index completion:^(TCHResult *result, TCHMessage *message) { 171 | if (result.isSuccessful) { 172 | resolve([RCTConvert TCHMessage:message]); 173 | } 174 | else { 175 | reject(@"get-message-error", @"Error occured while attempting to getMessageForConsumptionIndex.", result.error); 176 | } 177 | }]; 178 | } 179 | else { 180 | reject(@"get-message-error", @"Error occured while attempting to getMessage.", result.error); 181 | } 182 | }]; 183 | } 184 | 185 | RCT_REMAP_METHOD(setLastConsumedMessageIndex, channelSid:(NSString *)channelSid set_index:(NSNumber *)index setLastConsumedMessageIndex_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 186 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 187 | if (result.isSuccessful) { 188 | [messages setLastConsumedMessageIndex:index]; 189 | resolve(@[@TRUE]); 190 | } 191 | else { 192 | reject(@"set-last-consumed-message-error", @"Error occured while attempting to setLastConsumedMessageIndex.", result.error); 193 | } 194 | }]; 195 | } 196 | 197 | RCT_REMAP_METHOD(advanceLastConsumedMessageIndex, channelSid:(NSString *)channelSid advance_index:(NSNumber *)index advanceLastConsumedMessageIndex:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 198 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 199 | if (result.isSuccessful) { 200 | [messages advanceLastConsumedMessageIndex:index]; 201 | resolve(@[@TRUE]); 202 | } 203 | else { 204 | reject(@"advanceLastConsumedMessageIndex-error", @"Error occured while attempting to advanceLastConsumedMessageIndex.", result.error); 205 | } 206 | }]; 207 | } 208 | 209 | RCT_REMAP_METHOD(setAllMessagesConsumed, channelSid:(NSString *)channelSid setAllMessagesConsumed:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 210 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 211 | if (result.isSuccessful) { 212 | [messages setAllMessagesConsumed]; 213 | resolve(@[@TRUE]); 214 | } 215 | else { 216 | reject(@"setAllMessagesConsumed-error", @"Error occured while attempting to setAllMessagesConsumed.", result.error); 217 | } 218 | }]; 219 | } 220 | 221 | #pragma mark Message Instance Methods 222 | 223 | RCT_REMAP_METHOD(updateBody, channelSid:(NSString *)channelSid index:(NSNumber *)index body:(NSString *)body update_message_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 224 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 225 | if (result.isSuccessful) { 226 | [messages messageWithIndex:index completion:^(TCHResult *result, TCHMessage *message) { 227 | if (result.isSuccessful) { 228 | [message updateBody:body completion:^(TCHResult *result) { 229 | if (result.isSuccessful) { 230 | resolve(@[@TRUE]); 231 | } 232 | else { 233 | reject(@"update-message-error", @"Error occured while attempting to update the body of the message.", result.error); 234 | } 235 | }]; 236 | } 237 | else { 238 | reject(@"update-message-error", @"Error occured while attempting to update the body of the message.", result.error); 239 | } 240 | }]; 241 | } 242 | else { 243 | reject(@"update-message-error", @"Error occured while attempting to update the body of the message.", result.error); 244 | } 245 | }]; 246 | } 247 | 248 | 249 | RCT_REMAP_METHOD(setAttributes, channelSid:(NSString *)channelSid index:(NSNumber *)index attributes:(NSDictionary *)attributes set_attributes_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 250 | [self loadMessagesFromChannelSid:channelSid :^(TCHResult *result, TCHMessages *messages) { 251 | if (result.isSuccessful) { 252 | [messages messageWithIndex:index completion:^(TCHResult *result, TCHMessage *message) { 253 | if (result.isSuccessful) { 254 | [message setAttributes:attributes completion:^(TCHResult *result) { 255 | if (result.isSuccessful) { 256 | resolve(@[@TRUE]); 257 | } 258 | else { 259 | reject(@"set-attributes-error", @"Error occured while attempting to set attributes of the message.", result.error); 260 | } 261 | }]; 262 | } 263 | else { 264 | reject(@"set-attributes-error", @"Error occured while attempting to set attributes of the message.", result.error); 265 | } 266 | }]; 267 | } 268 | else { 269 | reject(@"set-attributes-error", @"Error occured while attempting to set attributes of the message.", result.error); 270 | } 271 | }]; 272 | } 273 | 274 | @end 275 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat/RCTTwilioChatChannels.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTTCHChannels.m 3 | // TwilioIPExample 4 | // 5 | // Created by Brad Bumbalough on 6/2/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTTwilioChatChannels.h" 10 | #import "RCTTwilioChatClient.h" 11 | #import "RCTConvert+TwilioChatClient.h" 12 | #import "RCTTwilioChatPaginator.h" 13 | #import 14 | 15 | @implementation RCTTwilioChatChannels 16 | 17 | RCT_EXPORT_MODULE(); 18 | 19 | + (void)loadChannelFromSid:(NSString *)sid :(void (^)(TCHResult *result, TCHChannel *channel))completion { 20 | TwilioChatClient *client = [[RCTTwilioChatClient sharedManager] client]; 21 | [[client channelsList] channelWithSidOrUniqueName:sid completion:completion]; 22 | } 23 | 24 | #pragma mark Channel Methods 25 | 26 | RCT_REMAP_METHOD(getUserChannels, userChannels_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 27 | TwilioChatClient *client = [[RCTTwilioChatClient sharedManager] client]; 28 | [[client channelsList] userChannelsWithCompletion:^(TCHResult *result, TCHChannelPaginator *paginator) { 29 | if (result.isSuccessful) { 30 | NSString *uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 31 | resolve(@{ 32 | @"sid":uuid, 33 | @"type": @"Channel", 34 | @"paginator": [RCTConvert TCHChannelPaginator:paginator] 35 | }); 36 | 37 | } 38 | else { 39 | reject(@"get-user-channels-error", @"Error occured while attempting to get the user channels.", result.error); 40 | } 41 | }]; 42 | } 43 | 44 | RCT_REMAP_METHOD(getPublicChannels, publicChannels_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 45 | TwilioChatClient *client = [[RCTTwilioChatClient sharedManager] client]; 46 | [[client channelsList] publicChannelsWithCompletion:^(TCHResult *result, TCHChannelDescriptorPaginator *paginator) { 47 | if (result.isSuccessful) { 48 | NSString *uuid = [RCTTwilioChatPaginator setPaginator:paginator]; 49 | resolve(@{ 50 | @"sid":uuid, 51 | @"type": @"ChannelDescriptor", 52 | @"paginator": [RCTConvert TCHChannelDescriptorPaginator:paginator] 53 | }); 54 | 55 | } 56 | else { 57 | reject(@"get-public-channels-error", @"Error occured while attempting to get the public channels.", result.error); 58 | } 59 | }]; 60 | } 61 | 62 | RCT_REMAP_METHOD(createChannel, options:(NSDictionary *)options create_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 63 | TwilioChatClient *client = [[RCTTwilioChatClient sharedManager] client]; 64 | [[client channelsList] createChannelWithOptions:options completion:^(TCHResult *result, TCHChannel *channel) { 65 | if (result.isSuccessful) { 66 | resolve([RCTConvert TCHChannel:channel]); 67 | } 68 | else { 69 | reject(@"create-error", @"Error occured while creating a new channel.", result.error); 70 | } 71 | }]; 72 | } 73 | 74 | RCT_REMAP_METHOD(getChannel, sidOrUniqueName:(NSString *)sidOrUniqueName sid_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 75 | TwilioChatClient *client = [[RCTTwilioChatClient sharedManager] client]; 76 | [[client channelsList] channelWithSidOrUniqueName:sidOrUniqueName completion:^(TCHResult *result, TCHChannel *channel) { 77 | if (result.isSuccessful) { 78 | resolve([RCTConvert TCHChannel:channel]); 79 | } 80 | else { 81 | reject(@"not-found", [NSString stringWithFormat:@"Channel could not be found with sid/uniquieName: %@.", sidOrUniqueName], result.error); 82 | } 83 | }]; 84 | } 85 | 86 | #pragma mark Channel Instance Methods 87 | 88 | RCT_REMAP_METHOD(synchronize, sid:(NSString *)sid synchronize_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 89 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 90 | if (result.isSuccessful) { 91 | [channel synchronizeWithCompletion:^(TCHResult *result) { 92 | if (result.isSuccessful) { 93 | resolve(@[@TRUE]); 94 | } 95 | else { 96 | reject(@"sync-error", @"Error occured during channel syncronization.", result.error); 97 | } 98 | }]; 99 | } 100 | else { 101 | reject(@"sync-error", @"Error occured during channel syncronization.", result.error); 102 | } 103 | }]; 104 | } 105 | 106 | RCT_REMAP_METHOD(setAttributes, sid:(NSString *)sid attributes:(NSDictionary *)attributes attributes_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 107 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 108 | if (result.isSuccessful) { 109 | [channel setAttributes:attributes completion:^(TCHResult *result) { 110 | if (result.isSuccessful) { 111 | resolve(@[@TRUE]); 112 | } 113 | else { 114 | reject(@"set-attributes-error", @"Error occuring while attempting to setAttributes on channel.", result.error); 115 | } 116 | }]; 117 | } 118 | else { 119 | reject(@"set-attributes-error", @"Error occuring while attempting to setAttributes on channel.", result.error); 120 | } 121 | }]; 122 | } 123 | 124 | RCT_REMAP_METHOD(setFriendlyName, sid:(NSString *)sid friendlyName:(NSString *)friendlyName attributes_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 125 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 126 | if (result.isSuccessful) { 127 | [channel setFriendlyName:friendlyName completion:^(TCHResult *result) { 128 | if (result.isSuccessful) { 129 | resolve(@[@TRUE]); 130 | } 131 | else { 132 | reject(@"set-friendlyName-error", @"Error occured while attempting to setFriendlyName on channel.", result.error); 133 | } 134 | }]; 135 | } 136 | else { 137 | reject(@"set-friendlyName-error", @"Error occured while attempting to setFriendlyName on channel.", result.error); 138 | } 139 | }]; 140 | } 141 | 142 | RCT_REMAP_METHOD(setUniqueName, sid:(NSString *)sid uniqueName:(NSString *)uniqueName attributes_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 143 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 144 | if (result.isSuccessful) { 145 | [channel setUniqueName:uniqueName completion:^(TCHResult *result) { 146 | if (result.isSuccessful) { 147 | resolve(@[@TRUE]); 148 | } 149 | else { 150 | reject(@"set-friendlyName-error", @"Error occured while attempting to setUniqueName on channel.", result.error); 151 | } 152 | }]; 153 | } 154 | else { 155 | reject(@"set-friendlyName-error", @"Error occured while attempting to setUniqueName on channel.", result.error); 156 | } 157 | }]; 158 | } 159 | 160 | RCT_REMAP_METHOD(join, sid:(NSString *)sid join_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 161 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 162 | if (result.isSuccessful) { 163 | [channel joinWithCompletion:^(TCHResult *result) { 164 | if (result.isSuccessful) { 165 | resolve(@[@TRUE]); 166 | } 167 | else { 168 | reject(@"join-channel-error", @"Error occured while attempting to join the channel.", result.error); 169 | } 170 | }]; 171 | } 172 | else { 173 | reject(@"join-channel-error", @"Error occured while attempting to join the channel.", result.error); 174 | } 175 | 176 | }]; 177 | } 178 | 179 | RCT_REMAP_METHOD(declineInvitation, sid:(NSString *)sid decline_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 180 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 181 | if (result.isSuccessful) { 182 | [channel declineInvitationWithCompletion:^(TCHResult *result) { 183 | if (result.isSuccessful) { 184 | resolve(@[@TRUE]); 185 | } 186 | else { 187 | reject(@"decline-invitation-error", @"Error occured while attempting to decline the invitation to join the channel.", result.error); 188 | } 189 | }]; 190 | } 191 | else { 192 | reject(@"decline-invitation-error", @"Error occured while attempting to decline the invitation to join the channel.", result.error); 193 | } 194 | }]; 195 | } 196 | 197 | RCT_REMAP_METHOD(leave, sid:(NSString *)sid leave_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 198 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 199 | if (result.isSuccessful) { 200 | [channel leaveWithCompletion:^(TCHResult *result) { 201 | if (result.isSuccessful) { 202 | resolve(@[@TRUE]); 203 | } 204 | else { 205 | reject(@"leave-channel-error", @"Error occured while attempting to leave the channel.", result.error); 206 | } 207 | }]; 208 | } 209 | else { 210 | reject(@"leave-channel-error", @"Error occured while attempting to leave the channel.", result.error); 211 | } 212 | }]; 213 | } 214 | 215 | RCT_REMAP_METHOD(destroy, sid:(NSString *)sid destroy_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 216 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 217 | if (result.isSuccessful) { 218 | [channel destroyWithCompletion:^(TCHResult *result) { 219 | if (result.isSuccessful) { 220 | resolve(@[@TRUE]); 221 | } 222 | else { 223 | reject(@"destroy-channel-error", @"Error occured while attempting to delete the channel.", result.error); 224 | } 225 | }]; 226 | } 227 | else { 228 | reject(@"destroy-channel-error", @"Error occured while attempting to delete the channel.", result.error); 229 | } 230 | }]; 231 | } 232 | 233 | RCT_REMAP_METHOD(getUnconsumedMessagesCount, sid:(NSString *)sid unconsumedCount_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 234 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 235 | if (result.isSuccessful) { 236 | [channel getUnconsumedMessagesCountWithCompletion:^(TCHResult *result, NSUInteger count) { 237 | if (result.isSuccessful) { 238 | resolve(@(count)); 239 | } 240 | else { 241 | reject(@"get-unconsumed-messages-count-error", @"Error occured while attempting to get unconsumed messages count.", result.error); 242 | } 243 | }]; 244 | } 245 | else { 246 | reject(@"get-unconsumed-messages-count-error", @"Error occured while attempting to get unconsumed messages count.", result.error); 247 | } 248 | }]; 249 | } 250 | 251 | RCT_REMAP_METHOD(getMessagesCount, sid:(NSString *)sid messagesCount_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 252 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 253 | if (result.isSuccessful) { 254 | [channel getMessagesCountWithCompletion:^(TCHResult *result, NSUInteger count) { 255 | if (result.isSuccessful) { 256 | resolve(@(count)); 257 | } 258 | else { 259 | reject(@"get-messages-count-error", @"Error occured while attempting to get message count.", result.error); 260 | } 261 | }]; 262 | } 263 | else { 264 | reject(@"get-messages-count-error", @"Error occured while attempting to get message count.", result.error); 265 | } 266 | }]; 267 | } 268 | 269 | RCT_REMAP_METHOD(getMembersCount, sid:(NSString *)sid membersCount_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 270 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 271 | if (result.isSuccessful) { 272 | [channel getMembersCountWithCompletion:^(TCHResult *result, NSUInteger count) { 273 | if (result.isSuccessful) { 274 | resolve(@(count)); 275 | } 276 | else { 277 | reject(@"get-members-count-error", @"Error occured while attempting to get members count.", result.error); 278 | } 279 | }]; 280 | 281 | } 282 | else { 283 | reject(@"get-members-count-error", @"Error occured while attempting to get members count.", result.error); 284 | } 285 | }]; 286 | } 287 | 288 | 289 | 290 | RCT_REMAP_METHOD(typing, sid:(NSString *)sid) { 291 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 292 | if (result.isSuccessful) { 293 | [channel typing]; 294 | } 295 | }]; 296 | } 297 | 298 | RCT_REMAP_METHOD(getMember, sid:(NSString *)sid identity:(NSString *)identity member_resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { 299 | [RCTTwilioChatChannels loadChannelFromSid:sid :^(TCHResult *result, TCHChannel *channel) { 300 | if (result.isSuccessful) { 301 | resolve([RCTConvert TCHMember:[channel memberWithIdentity:identity]]); 302 | } 303 | else { 304 | reject(@"get-member", @"Error occured while attempting to get member.", result.error); 305 | 306 | } 307 | }]; 308 | } 309 | 310 | @end 311 | -------------------------------------------------------------------------------- /ios/RCTTwilioChat.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D6F099C1DF1EBE200611A19 /* RCTTwilioChatPaginator.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D6F099B1DF1EBE200611A19 /* RCTTwilioChatPaginator.m */; }; 11 | 1DAE2D361D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D2B1D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.m */; }; 12 | 1DAE2D371D086CFD00D3FE06 /* RCTTwilioAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D2D1D086CFD00D3FE06 /* RCTTwilioAccessManager.m */; }; 13 | 1DAE2D381D086CFD00D3FE06 /* RCTTwilioChatChannels.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D2F1D086CFD00D3FE06 /* RCTTwilioChatChannels.m */; }; 14 | 1DAE2D391D086CFD00D3FE06 /* RCTTwilioChatClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D311D086CFD00D3FE06 /* RCTTwilioChatClient.m */; }; 15 | 1DAE2D3A1D086CFD00D3FE06 /* RCTTwilioChatMembers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D331D086CFD00D3FE06 /* RCTTwilioChatMembers.m */; }; 16 | 1DAE2D3B1D086CFD00D3FE06 /* RCTTwilioChatMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DAE2D351D086CFD00D3FE06 /* RCTTwilioChatMessages.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 1DAE2D1C1D086CD600D3FE06 /* CopyFiles */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = "include/$(PRODUCT_NAME)"; 24 | dstSubfolderSpec = 16; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1D6F099A1DF1EBE100611A19 /* RCTTwilioChatPaginator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioChatPaginator.h; sourceTree = ""; }; 33 | 1D6F099B1DF1EBE200611A19 /* RCTTwilioChatPaginator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioChatPaginator.m; sourceTree = ""; }; 34 | 1DAE2D1E1D086CD600D3FE06 /* libRCTTwilioChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTTwilioChat.a; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 1DAE2D2A1D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+TwilioChatClient.h"; sourceTree = ""; }; 36 | 1DAE2D2B1D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+TwilioChatClient.m"; sourceTree = ""; }; 37 | 1DAE2D2C1D086CFD00D3FE06 /* RCTTwilioAccessManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioAccessManager.h; sourceTree = ""; }; 38 | 1DAE2D2D1D086CFD00D3FE06 /* RCTTwilioAccessManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioAccessManager.m; sourceTree = ""; }; 39 | 1DAE2D2E1D086CFD00D3FE06 /* RCTTwilioChatChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioChatChannels.h; sourceTree = ""; }; 40 | 1DAE2D2F1D086CFD00D3FE06 /* RCTTwilioChatChannels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioChatChannels.m; sourceTree = ""; }; 41 | 1DAE2D301D086CFD00D3FE06 /* RCTTwilioChatClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioChatClient.h; sourceTree = ""; }; 42 | 1DAE2D311D086CFD00D3FE06 /* RCTTwilioChatClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioChatClient.m; sourceTree = ""; }; 43 | 1DAE2D321D086CFD00D3FE06 /* RCTTwilioChatMembers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioChatMembers.h; sourceTree = ""; }; 44 | 1DAE2D331D086CFD00D3FE06 /* RCTTwilioChatMembers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioChatMembers.m; sourceTree = ""; }; 45 | 1DAE2D341D086CFD00D3FE06 /* RCTTwilioChatMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTwilioChatMessages.h; sourceTree = ""; }; 46 | 1DAE2D351D086CFD00D3FE06 /* RCTTwilioChatMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTwilioChatMessages.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 1DAE2D1B1D086CD600D3FE06 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 1DAE2D151D086CD600D3FE06 = { 61 | isa = PBXGroup; 62 | children = ( 63 | 1DAE2D201D086CD600D3FE06 /* RCTTwilioChat */, 64 | 1DAE2D1F1D086CD600D3FE06 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 1DAE2D1F1D086CD600D3FE06 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 1DAE2D1E1D086CD600D3FE06 /* libRCTTwilioChat.a */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 1DAE2D201D086CD600D3FE06 /* RCTTwilioChat */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 1DAE2D2A1D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.h */, 80 | 1DAE2D2B1D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.m */, 81 | 1DAE2D2C1D086CFD00D3FE06 /* RCTTwilioAccessManager.h */, 82 | 1DAE2D2D1D086CFD00D3FE06 /* RCTTwilioAccessManager.m */, 83 | 1DAE2D2E1D086CFD00D3FE06 /* RCTTwilioChatChannels.h */, 84 | 1DAE2D2F1D086CFD00D3FE06 /* RCTTwilioChatChannels.m */, 85 | 1DAE2D301D086CFD00D3FE06 /* RCTTwilioChatClient.h */, 86 | 1DAE2D311D086CFD00D3FE06 /* RCTTwilioChatClient.m */, 87 | 1DAE2D321D086CFD00D3FE06 /* RCTTwilioChatMembers.h */, 88 | 1DAE2D331D086CFD00D3FE06 /* RCTTwilioChatMembers.m */, 89 | 1D6F099A1DF1EBE100611A19 /* RCTTwilioChatPaginator.h */, 90 | 1D6F099B1DF1EBE200611A19 /* RCTTwilioChatPaginator.m */, 91 | 1DAE2D341D086CFD00D3FE06 /* RCTTwilioChatMessages.h */, 92 | 1DAE2D351D086CFD00D3FE06 /* RCTTwilioChatMessages.m */, 93 | ); 94 | path = RCTTwilioChat; 95 | sourceTree = ""; 96 | }; 97 | /* End PBXGroup section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | 1DAE2D1D1D086CD600D3FE06 /* RCTTwilioChat */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = 1DAE2D271D086CD600D3FE06 /* Build configuration list for PBXNativeTarget "RCTTwilioChat" */; 103 | buildPhases = ( 104 | 1DAE2D1A1D086CD600D3FE06 /* Sources */, 105 | 1DAE2D1B1D086CD600D3FE06 /* Frameworks */, 106 | 1DAE2D1C1D086CD600D3FE06 /* CopyFiles */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = RCTTwilioChat; 113 | productName = RCTTwilioChat; 114 | productReference = 1DAE2D1E1D086CD600D3FE06 /* libRCTTwilioChat.a */; 115 | productType = "com.apple.product-type.library.static"; 116 | }; 117 | /* End PBXNativeTarget section */ 118 | 119 | /* Begin PBXProject section */ 120 | 1DAE2D161D086CD600D3FE06 /* Project object */ = { 121 | isa = PBXProject; 122 | attributes = { 123 | LastUpgradeCheck = 0730; 124 | ORGANIZATIONNAME = "Brad Bumbalough"; 125 | TargetAttributes = { 126 | 1DAE2D1D1D086CD600D3FE06 = { 127 | CreatedOnToolsVersion = 7.3.1; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 1DAE2D191D086CD600D3FE06 /* Build configuration list for PBXProject "RCTTwilioChat" */; 132 | compatibilityVersion = "Xcode 3.2"; 133 | developmentRegion = English; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | ); 138 | mainGroup = 1DAE2D151D086CD600D3FE06; 139 | productRefGroup = 1DAE2D1F1D086CD600D3FE06 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 1DAE2D1D1D086CD600D3FE06 /* RCTTwilioChat */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXSourcesBuildPhase section */ 149 | 1DAE2D1A1D086CD600D3FE06 /* Sources */ = { 150 | isa = PBXSourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 1DAE2D381D086CFD00D3FE06 /* RCTTwilioChatChannels.m in Sources */, 154 | 1DAE2D361D086CFD00D3FE06 /* RCTConvert+TwilioChatClient.m in Sources */, 155 | 1DAE2D3A1D086CFD00D3FE06 /* RCTTwilioChatMembers.m in Sources */, 156 | 1DAE2D391D086CFD00D3FE06 /* RCTTwilioChatClient.m in Sources */, 157 | 1D6F099C1DF1EBE200611A19 /* RCTTwilioChatPaginator.m in Sources */, 158 | 1DAE2D371D086CFD00D3FE06 /* RCTTwilioAccessManager.m in Sources */, 159 | 1DAE2D3B1D086CFD00D3FE06 /* RCTTwilioChatMessages.m in Sources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXSourcesBuildPhase section */ 164 | 165 | /* Begin XCBuildConfiguration section */ 166 | 1DAE2D251D086CD600D3FE06 /* Debug */ = { 167 | isa = XCBuildConfiguration; 168 | buildSettings = { 169 | ALWAYS_SEARCH_USER_PATHS = NO; 170 | CLANG_ANALYZER_NONNULL = YES; 171 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 172 | CLANG_CXX_LIBRARY = "libc++"; 173 | CLANG_ENABLE_MODULES = YES; 174 | CLANG_ENABLE_OBJC_ARC = YES; 175 | CLANG_WARN_BOOL_CONVERSION = YES; 176 | CLANG_WARN_CONSTANT_CONVERSION = YES; 177 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 178 | CLANG_WARN_EMPTY_BODY = YES; 179 | CLANG_WARN_ENUM_CONVERSION = YES; 180 | CLANG_WARN_INT_CONVERSION = YES; 181 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 182 | CLANG_WARN_UNREACHABLE_CODE = YES; 183 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 184 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 185 | COPY_PHASE_STRIP = NO; 186 | DEBUG_INFORMATION_FORMAT = dwarf; 187 | ENABLE_STRICT_OBJC_MSGSEND = YES; 188 | ENABLE_TESTABILITY = YES; 189 | GCC_C_LANGUAGE_STANDARD = gnu99; 190 | GCC_DYNAMIC_NO_PIC = NO; 191 | GCC_NO_COMMON_BLOCKS = YES; 192 | GCC_OPTIMIZATION_LEVEL = 0; 193 | GCC_PREPROCESSOR_DEFINITIONS = ( 194 | "DEBUG=1", 195 | "$(inherited)", 196 | ); 197 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 198 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 199 | GCC_WARN_UNDECLARED_SELECTOR = YES; 200 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 201 | GCC_WARN_UNUSED_FUNCTION = YES; 202 | GCC_WARN_UNUSED_VARIABLE = YES; 203 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 204 | MTL_ENABLE_DEBUG_INFO = YES; 205 | ONLY_ACTIVE_ARCH = YES; 206 | SDKROOT = iphoneos; 207 | }; 208 | name = Debug; 209 | }; 210 | 1DAE2D261D086CD600D3FE06 /* Release */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 216 | CLANG_CXX_LIBRARY = "libc++"; 217 | CLANG_ENABLE_MODULES = YES; 218 | CLANG_ENABLE_OBJC_ARC = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 222 | CLANG_WARN_EMPTY_BODY = YES; 223 | CLANG_WARN_ENUM_CONVERSION = YES; 224 | CLANG_WARN_INT_CONVERSION = YES; 225 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 226 | CLANG_WARN_UNREACHABLE_CODE = YES; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 229 | COPY_PHASE_STRIP = NO; 230 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 231 | ENABLE_NS_ASSERTIONS = NO; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu99; 234 | GCC_NO_COMMON_BLOCKS = YES; 235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 237 | GCC_WARN_UNDECLARED_SELECTOR = YES; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 239 | GCC_WARN_UNUSED_FUNCTION = YES; 240 | GCC_WARN_UNUSED_VARIABLE = YES; 241 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 242 | MTL_ENABLE_DEBUG_INFO = NO; 243 | SDKROOT = iphoneos; 244 | VALIDATE_PRODUCT = YES; 245 | }; 246 | name = Release; 247 | }; 248 | D2EA9F3D1EC1AEE30040EF5B /* Debug */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | COPY_PHASE_STRIP = NO; 252 | FRAMEWORK_SEARCH_PATHS = ( 253 | "$(inherited)", 254 | "$(PROJECT_DIR)/../../../ios/**", 255 | ); 256 | GCC_DYNAMIC_NO_PIC = NO; 257 | GCC_OPTIMIZATION_LEVEL = 0; 258 | HEADER_SEARCH_PATHS = ( 259 | "$(SRCROOT)/../../react-native/React/**", 260 | "$(SRCROOT)/../../../ios/**", 261 | ); 262 | LIBRARY_SEARCH_PATHS = "$(inherited}"; 263 | PRODUCT_NAME = RCTTwilioChat; 264 | }; 265 | name = Debug; 266 | }; 267 | D2EA9F3E1EC1AEE30040EF5B /* Release */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | COPY_PHASE_STRIP = YES; 271 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 272 | FRAMEWORK_SEARCH_PATHS = ( 273 | "$(inherited)", 274 | "$(PROJECT_DIR)/../../../ios/**", 275 | ); 276 | HEADER_SEARCH_PATHS = ( 277 | "$(SRCROOT)/../../react-native/React/**", 278 | "$(SRCROOT)/../../../ios/**", 279 | ); 280 | LIBRARY_SEARCH_PATHS = "$(inherited}"; 281 | PRODUCT_NAME = RCTTwilioChat; 282 | }; 283 | name = Release; 284 | }; 285 | /* End XCBuildConfiguration section */ 286 | 287 | /* Begin XCConfigurationList section */ 288 | 1DAE2D191D086CD600D3FE06 /* Build configuration list for PBXProject "RCTTwilioChat" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | 1DAE2D251D086CD600D3FE06 /* Debug */, 292 | 1DAE2D261D086CD600D3FE06 /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | 1DAE2D271D086CD600D3FE06 /* Build configuration list for PBXNativeTarget "RCTTwilioChat" */ = { 298 | isa = XCConfigurationList; 299 | buildConfigurations = ( 300 | D2EA9F3D1EC1AEE30040EF5B /* Debug */, 301 | D2EA9F3E1EC1AEE30040EF5B /* Release */, 302 | ); 303 | defaultConfigurationIsVisible = 0; 304 | defaultConfigurationName = Release; 305 | }; 306 | /* End XCConfigurationList section */ 307 | }; 308 | rootObject = 1DAE2D161D086CD600D3FE06 /* Project object */; 309 | } 310 | --------------------------------------------------------------------------------