├── 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 | 
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 | [](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 |
--------------------------------------------------------------------------------