From 0f16afe5171b8b4700c7c8f6b8843540c932bc2a Mon Sep 17 00:00:00 2001 From: matrixbirds Date: Mon, 8 Jul 2019 13:05:17 +0800 Subject: [PATCH] init --- .gitignore | 72 ++ .npmignore | 83 ++ README.md | 36 + agora-react-native-rtm.podspec | 20 + android/build.gradle | 78 ++ android/settings.gradle | 3 + android/src/main/AndroidManifest.xml | 17 + .../io/agora/agora_rtm/AgoraRTMConstants.java | 22 + .../io/agora/agora_rtm/AgoraRTMModule.java | 720 ++++++++++++++++++ .../io/agora/agora_rtm/AgoraRTMPackage.java | 31 + android/src/main/res/values/strings.xml | 3 + ios/AgoraRTM.xcodeproj/project.pbxproj | 338 ++++++++ ios/src/AgoraConst.h | 28 + ios/src/AgoraRTM.h | 19 + ios/src/AgoraRTM.m | 590 ++++++++++++++ package.json | 70 ++ src/RtmEngine.ts | 320 ++++++++ src/types.d.ts | 247 ++++++ tsconfig.json | 26 + typedoc.json | 4 + 20 files changed, 2727 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 README.md create mode 100644 agora-react-native-rtm.podspec create mode 100644 android/build.gradle create mode 100644 android/settings.gradle create mode 100644 android/src/main/AndroidManifest.xml create mode 100644 android/src/main/java/io/agora/agora_rtm/AgoraRTMConstants.java create mode 100644 android/src/main/java/io/agora/agora_rtm/AgoraRTMModule.java create mode 100644 android/src/main/java/io/agora/agora_rtm/AgoraRTMPackage.java create mode 100644 android/src/main/res/values/strings.xml create mode 100644 ios/AgoraRTM.xcodeproj/project.pbxproj create mode 100644 ios/src/AgoraConst.h create mode 100644 ios/src/AgoraRTM.h create mode 100644 ios/src/AgoraRTM.m create mode 100644 package.json create mode 100644 src/RtmEngine.ts create mode 100644 src/types.d.ts create mode 100644 tsconfig.json create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f89aae --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle + +# CocoaPods +/ios/Pods/ +.classpath +.project +.settings +android/gradle +android/gradlew* +package-lock.json +yarn.lock +*.sh +android/.gradle +*.*~ +android/proguard-rules.pro +docs +lib diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..ca09b4d --- /dev/null +++ b/.npmignore @@ -0,0 +1,83 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle + +# CocoaPods +/ios/Pods/ +Podfile.lock +example +android/build/ +android/.classpath +android/.gradle +android/.idea +android/.project +android/.DS_Store +android/*.properties +android/gradle* +android/*.pro +android/*.iml +ios/.DS_Store +ios/AgoraRTM.xcodeproj +package-lock.json +tsconfig.* +typedoc.* +.git +.gitignore +.vscode +*.sh +android/.gradle +*.*~ +src/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ac1b3a --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# agora-react-native-rtm + +## Description + +The agora-react-native-rtm is an open-source wrapper for react-native developers. This SDK takes advantage of React Native and Agora RTM SDKs on Android/iOS. + +## Compatibility + * `>= react native 0.55.x` + * `iOS SDK 8.0+` + * `Android 5.0+ x86 arm64 armv7` + +## Installation + +Install with npm: + + `npm install --save agora-react-native-rtm` + +Or, install with yarn: + + `yarn add agora-react-native-rtm` + +Either way, then link with: + + `react-native link agora-react-native-rtm` + +## Documentation + * [examples](https://github.com/AgoraIO/RN-SDK-RTM/tree/master/examples) + * [api docs](https://agoraio.github.io/RN-SDK-RTM/latest/) + * [Agora RTM 中文文档](https://docs-preview.agoralab.co/cn/Real-time-Messaging/RTM_product?platform=All%20Platforms) + * [Agora RTM English](https://docs-preview.agoralab.co/en/Real-time-Messaging/RTM_product?platform=All%20Platforms) + + +## Resources +* Complete [API documentation](https://docs.agora.io/en/) at the Developer Center +* [File bugs about this sample](https://github.com/AgoraIO/RN-SDK-RTM/issues) +* [React Native Getting Started](https://facebook.github.io/react-native/docs/getting-started.html) diff --git a/agora-react-native-rtm.podspec b/agora-react-native-rtm.podspec new file mode 100644 index 0000000..abb651f --- /dev/null +++ b/agora-react-native-rtm.podspec @@ -0,0 +1,20 @@ +require 'json' + +package = JSON.parse File.read File.join __dir__, "package.json" +Pod::Spec.new do |s| + s.name = package["name"] + s.version = package["version"] + s.summary = package["summary"] + s.description = package["description"] + + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["authors"] + s.platform = :ios + s.source = { :git => package["repository"]["url"], :tag => "#{s.version}" } + s.source_files = 'ios/src/**/*.{h,m}' + + s.dependency 'AgoraRtm_iOS', '0.9.3' + s.dependency 'React' + s.ios.deployment_target = '8.0' +end diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..90755a0 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,78 @@ +apply plugin: 'com.android.library' + +allprojects { + gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + options.encoding = 'utf-8' + options.compilerArgs << "-Xlint:deprecation" + } + } +} + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.2' + } +} + +def DEFAULT_COMPILE_SDK_VERSION = 27 +def DEFAULT_BUILD_TOOLS_VERSION = "27.0.3" +def DEFAULT_TARGET_SDK_VERSION = 27 +def DEFAULT_ANDROID_SUPPORT_VERSION = "27.1.0" + +android { + compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION + buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION + + defaultConfig { + minSdkVersion 16 + targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + splits { + abi { + enable true + + reset() + + include "armeabi-v7a", "arm64-v8a", "x86" + // Specify that we do not want an additional universal SDK + universalApk false + } + } +} + +dependencies { + def androidSupportVersion = rootProject.hasProperty("androidSupportVersion") ? rootProject.androidSupportVersion : DEFAULT_ANDROID_SUPPORT_VERSION + // from internet + implementation "com.android.support:appcompat-v7:$androidSupportVersion" + implementation 'io.agora.rtm:rtm-sdk:0.9.3' + // from node_modules + implementation "com.facebook.react:react-native:+" +} + +repositories { + mavenCentral() + mavenLocal() + maven { + url "$rootDir/../node_modules/agora-react-native-rtm/android" + } + jcenter() + google() +} + diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..97be8c7 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,3 @@ +include ':agora_rtm' + + diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f88612c --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/android/src/main/java/io/agora/agora_rtm/AgoraRTMConstants.java b/android/src/main/java/io/agora/agora_rtm/AgoraRTMConstants.java new file mode 100644 index 0000000..ec2d5ce --- /dev/null +++ b/android/src/main/java/io/agora/agora_rtm/AgoraRTMConstants.java @@ -0,0 +1,22 @@ +package io.agora.agora_rtm; + +public final class AgoraRTMConstants { + public static final String AG_ERROR = "ag_rtm_error"; + public static final String AG_CONNECTIONSTATECHANGED = "ag_rtm_connectionStateChanged"; + public static final String AG_MESSAGERECEIVED = "ag_rtm_messageReceived"; + public static final String AG_LOCALINVITATIONRECEIVEDBYPEER = "ag_rtm_localInvitationReceivedByPeer"; + public static final String AG_LOCALINVITATIONACCEPTED = "ag_rtm_localInvitationAccepted"; + public static final String AG_LOCALINVITATIONREFUSED = "ag_rtm_localInvitationRefused"; + public static final String AG_LOCALINVITATIONCANCELED = "ag_rtm_localInvitationCanceled"; + public static final String AG_LOCALINVITATIONFAILURE = "ag_rtm_localInvitationFailure"; + public static final String AG_REMOTEINVITATIONFAILURE = "ag_rtm_remoteInvitationFailure"; + public static final String AG_REMOTEINVITATIONRECEIVED = "ag_rtm_remoteInvitationReceived"; + public static final String AG_REMOTEINVITATIONACCEPTED = "ag_rtm_remoteInvitationAccepted"; + public static final String AG_REMOTEINVITATIONREFUSED = "ag_rtm_remoteInvitationRefused"; + public static final String AG_REMOTEINVITATIONCANCELED = "ag_rtm_remoteInvitationCanceled"; + public static final String AG_CHANNELMESSAGERECEVIED = "ag_rtm_channelMessageReceived"; + public static final String AG_CHANNELMEMBERJOINED = "ag_rtm_channelMemberJoined"; + public static final String AG_CHANNELMEMBERLEFT = "ag_rtm_channelMemberLeft"; + public static final String AG_TOKEN_EXPIRED = "ag_rtm_tokenExpired"; + +} diff --git a/android/src/main/java/io/agora/agora_rtm/AgoraRTMModule.java b/android/src/main/java/io/agora/agora_rtm/AgoraRTMModule.java new file mode 100644 index 0000000..98821a2 --- /dev/null +++ b/android/src/main/java/io/agora/agora_rtm/AgoraRTMModule.java @@ -0,0 +1,720 @@ +package io.agora.agora_rtm; + +import android.support.annotation.Nullable; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.modules.core.DeviceEventManagerModule; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import io.agora.rtm.ErrorInfo; +import io.agora.rtm.LocalInvitation; +import io.agora.rtm.RemoteInvitation; +import io.agora.rtm.ResultCallback; +import io.agora.rtm.RtmAttribute; +import io.agora.rtm.RtmCallEventListener; +import io.agora.rtm.RtmCallManager; +import io.agora.rtm.RtmChannel; +import io.agora.rtm.RtmChannelListener; +import io.agora.rtm.RtmChannelMember; +import io.agora.rtm.RtmClient; +import io.agora.rtm.RtmClientListener; +import io.agora.rtm.RtmMessage; +import io.agora.rtm.SendMessageOptions; + +public class AgoraRTMModule extends ReactContextBaseJavaModule + implements RtmClientListener, RtmCallEventListener, RtmChannelListener { + + private Map localInvitations = new HashMap<>(); + private Map remoteInvitations = new HashMap<>(); + private Map channels = new HashMap<>(); + + private RtmClient rtmClient; + private RtmCallManager rtmCallManager; + + private boolean hasListeners = false; + + private void startObserving () { + hasListeners = true; + } + + private void stopObserving () { + hasListeners = false; + } + + private void sendEvent(String eventName, + @Nullable WritableMap params) { + if (hasListeners) { + getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit(eventName, params); + } + } + + public AgoraRTMModule(ReactApplicationContext ctx) { + super(ctx); + } + + @Override + public String getName() { + return "AgoraRTM"; + } + + // init AgoraRTM instance, set event observing and init it's internal resources + @ReactMethod + public void init(String appID) { + try { + rtmClient = RtmClient.createInstance(getReactApplicationContext(), appID, this); + rtmCallManager = rtmClient.getRtmCallManager(); + rtmCallManager.setEventListener(this); + startObserving(); + } catch (Exception exception) { + WritableMap params = Arguments.createMap(); + params.putString("api", "init"); + params.putString("message", exception.getMessage()); + sendEvent(AgoraRTMConstants.AG_ERROR, params); + } + } + + // destroy AgoraRTM instance, remove event observing and it's internal resources + @ReactMethod + public void destroy() { + stopObserving(); + localInvitations.clear(); + localInvitations = null; + remoteInvitations.clear(); + remoteInvitations = null; + for (Map.Entry ite: channels.entrySet()) { + ite.getValue().release(); + } + channels.clear(); + channels = null; + rtmCallManager = null; + rtmClient.release(); + rtmClient = null; + } + + // login + @ReactMethod + public void login(final ReadableMap params, final Promise promise) { + String token = null; + if (params.hasKey("token")) { + token = params.getString("token"); + } + final String userId = params.getString("uid"); + rtmClient.login(token != null ? token : null, userId, new ResultCallback() { + @Override + public void onSuccess(Void args) { + promise.resolve(null); + } + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // logout + @ReactMethod + public void logout(final Promise promise) { + rtmClient.logout(new ResultCallback() { + @Override + public void onSuccess(Void args) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // renewToken + @ReactMethod + public void renewToken(final String token, final Promise promise) { + rtmClient.renewToken(token, new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(aVoid); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // sendMessageToPeer + @ReactMethod + public void sendMessageToPeer(final ReadableMap params, final Promise promise) { + RtmMessage rtmMessage = rtmClient.createMessage(); + String peerId = params.getString("peerId"); + String message = params.getString("text"); + Boolean enableOffline = params.getBoolean("offline"); + SendMessageOptions options = new SendMessageOptions(); + options.enableOfflineMessaging = enableOffline; + rtmMessage.setText(message); + rtmClient.sendMessageToPeer(peerId, rtmMessage, options, new ResultCallback() { + @Override + public void onSuccess(Void args) { + promise.resolve(args); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // join channel + @ReactMethod + public void joinChannel(final String channelId, final Promise promise) { + final RtmChannel rtmChannel = rtmClient.createChannel(channelId, this); + if (null == rtmChannel) { + promise.reject("-1", "channel_create_failed"); + } else { + rtmChannel.join(new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + channels.put(channelId, rtmChannel); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + channels.put("channelId", rtmChannel); + } + } + + // leave channel + @ReactMethod + public void leaveChannel(final String channelId, final Promise promise) { + final RtmChannel rtmChannel = channels.get(channelId); + if (null == rtmChannel) { + promise.reject("-1", "channel_not_found"); + } else { + rtmChannel.leave(new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + rtmChannel.release(); + channels.remove(channelId); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + } + + // get channel members by channelId + @ReactMethod + public void getChannelMembersBychannelId(final String channelId, final Promise promise) { + RtmChannel rtmChannel = channels.get(channelId); + if (null == rtmChannel) { + promise.reject("-1", "channel_not_found"); + } else { + rtmChannel.getMembers(new ResultCallback>() { + @Override + public void onSuccess(List rtmChannelMembers) { + WritableArray members = Arguments.createArray(); + for (RtmChannelMember member: rtmChannelMembers) { + WritableMap memberData = Arguments.createMap(); + memberData.putString("uid", member.getUserId()); + memberData.putString("channelId", member.getChannelId()); + members.pushMap(memberData); + } + WritableMap params = Arguments.createMap(); + params.putArray("members", members); + promise.resolve(params); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + } + + // sned channel message by channel id + @ReactMethod + public void sendMessageByChannelId(ReadableMap params, final Promise promise) { + final String channelId = params.getString("channelId"); + final String text = params.getString("text"); + RtmChannel rtmChannel = channels.get(channelId); + + if (null == rtmChannel) { + promise.reject("-1", "channel_not_found"); + } else { + RtmMessage rtmMessage = rtmClient.createMessage(); + rtmMessage.setText(text); + rtmChannel.sendMessage(rtmMessage, new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + } + + // query peer online status with ids + @ReactMethod + public void queryPeersOnlineStatus(final ReadableMap params, final Promise promise) { + final ReadableArray ids = params.getArray("ids"); + Set sets = new HashSet(); + for (int i = 0; i < ids.size(); i++) { + sets.add(ids.getString(i)); + } + rtmClient.queryPeersOnlineStatus(sets, new ResultCallback>() { + @Override + public void onSuccess(Map result) { + WritableArray items = Arguments.createArray(); + Set keys = result.keySet(); + for (String key : keys) { + boolean online = result.get(key); + String uid = key; + WritableMap item = Arguments.createMap(); + item.putString("uid", uid); + item.putBoolean("online", online); + items.pushMap(item); + } + WritableMap params = Arguments.createMap(); + params.putArray("items", items); + promise.resolve(params); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + + // setup local user attributes + @ReactMethod + public void setLocalUserAttributes(ReadableMap params, final Promise promise) { + ReadableArray attributesParam = params.getArray("attributes"); + List attributes = new LinkedList(); + for (int i = 0; i < attributesParam.size(); i++) { + RtmAttribute rtmAttribute = new RtmAttribute(); + ReadableMap item = attributesParam.getMap(i); + rtmAttribute.setKey(item.getString("key")); + rtmAttribute.setValue(item.getString("value")); + attributes.add(rtmAttribute); + } + rtmClient.setLocalUserAttributes(attributes, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // replace local user attributes + @ReactMethod + public void replaceLocalUserAttributes(ReadableMap params, final Promise promise) { + ReadableArray attributesParam = params.getArray("attributes"); + List attributes = new LinkedList(); + for (int i = 0; i < attributesParam.size(); i++) { + RtmAttribute rtmAttribute = new RtmAttribute(); + ReadableMap item = attributesParam.getMap(i); + rtmAttribute.setKey(item.getString("key")); + rtmAttribute.setValue(item.getString("value")); + attributes.add(rtmAttribute); + } + rtmClient.addOrUpdateLocalUserAttributes(attributes, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // remove local user attributes by keys + @ReactMethod + public void removeLocalUserAttributesByKeys(ReadableMap params, final Promise promise) { + List list = new LinkedList<>(); + ReadableArray array = params.getArray("keys"); + for (int i = 0; i < array.size(); i++) { + list.add(array.getString(i)); + } + rtmClient.deleteLocalUserAttributesByKeys(list, new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // remove all local user attributes + @ReactMethod + public void removeAllLocalUserAttributes(final Promise promise) { + rtmClient.clearLocalUserAttributes(new ResultCallback() { + @Override + public void onSuccess(Void aVoid) { + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // get local user attributes by uid + @ReactMethod + public void getUserAttributesByUid(final String userId, final Promise promise) { + rtmClient.getUserAttributes(userId, new ResultCallback>() { + @Override + public void onSuccess(List rtmAttributes) { + WritableMap userAttributes = Arguments.createMap(); + for (RtmAttribute attribute: rtmAttributes) { + userAttributes.putString(attribute.getKey(), attribute.getValue()); + } + WritableMap data = Arguments.createMap(); + data.putString("uid", userId); + data.putMap("attributes", userAttributes); + promise.resolve(data); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + + // send local invitation + @ReactMethod + public void sendLocalInvitation (ReadableMap params, final Promise promise) { + final String calleeId = params.getString("uid"); + final LocalInvitation localInvitation = rtmCallManager.createLocalInvitation(calleeId); + final String channelId = params.getString("channelId"); + String content = null; + if(params.hasKey("content")) { + content = params.getString("content"); + localInvitation.setContent(content); + } + localInvitation.setChannelId(channelId); + rtmCallManager.sendLocalInvitation(localInvitation, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + localInvitations.put(calleeId, localInvitation); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } + + // cancel local invitation + @ReactMethod + public void cancelLocalInvitation (ReadableMap params, final Promise promise) { + final String calleeId = params.getString("uid"); + if (null != localInvitations.get(calleeId)) { + final LocalInvitation localInvitation = localInvitations.get(calleeId); + String channelId = params.getString("channelId"); + if (params.hasKey("content")) { + String content = params.getString("content"); + localInvitation.setContent(content); + } + localInvitation.setChannelId(channelId); + rtmCallManager.cancelLocalInvitation(localInvitation, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + localInvitations.put(calleeId, localInvitation); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } else { + promise.reject("-1", "local_invitation_not_found"); + } + } + + // accept remote invitation + @ReactMethod + public void acceptRemoteInvitation (ReadableMap params, final Promise promise) { + final String calleeId = params.getString("uid"); + if (null != remoteInvitations.get(calleeId)) { + final RemoteInvitation remoteInvitation = remoteInvitations.get(calleeId); + if (params.hasKey("response")) { + final String response = params.getString("response"); + remoteInvitation.setResponse(response); + } + rtmCallManager.acceptRemoteInvitation(remoteInvitation, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + remoteInvitations.remove(remoteInvitation); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } else { + promise.reject(Integer.toString(-1), "remote_invitation_not_found"); + } + } + + + // refuse remote invitation + @ReactMethod + public void refuseRemoteInvitation (ReadableMap params, final Promise promise) { + final String calleeId = params.getString("uid"); + if (null != remoteInvitations.get(calleeId)) { + final RemoteInvitation remoteInvitation = remoteInvitations.get(calleeId); + if (params.hasKey("response")) { + final String response = params.getString("response"); + remoteInvitation.setResponse(response); + } + rtmCallManager.refuseRemoteInvitation(remoteInvitation, new ResultCallback () { + @Override + public void onSuccess(Void aVoid) { + remoteInvitations.remove(remoteInvitation); + promise.resolve(null); + } + + @Override + public void onFailure(ErrorInfo errorInfo) { + promise.reject(Integer.toString(errorInfo.getErrorCode()), errorInfo.getErrorDescription()); + } + }); + } else { + promise.reject("-1", "remote_invitation_not_found"); + } + } + + // RtmClientListener + @Override + public void onConnectionStateChanged(int state, int reason) { + WritableMap params = Arguments.createMap(); + params.putInt("state", state); + params.putInt("reason", reason); + sendEvent(AgoraRTMConstants.AG_CONNECTIONSTATECHANGED, + params); + } + + // p2p message received + @Override + public void onMessageReceived(RtmMessage rtmMessage, String peerId) { + WritableMap params = Arguments.createMap(); + params.putString("text", rtmMessage.getText()); + params.putString("ts", Long.toString(rtmMessage.getServerReceivedTs())); + params.putBoolean("offline", rtmMessage.isOfflineMessage()); + params.putString("peerId", peerId); + sendEvent(AgoraRTMConstants.AG_MESSAGERECEIVED, params); + } + + @Override + public void onTokenExpired() { + WritableMap params = Arguments.createMap(); + params.putString("message", "token_expired"); + sendEvent(AgoraRTMConstants.AG_TOKEN_EXPIRED, params); + } + + // RtmCallEventListener + @Override + public void onLocalInvitationReceivedByPeer(LocalInvitation localInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("calleeId", localInvitation.getCalleeId()); + params.putString("content", localInvitation.getContent()); + params.putInt("state", localInvitation.getState()); + params.putString("channelId", localInvitation.getChannelId()); + params.putString("response", localInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_LOCALINVITATIONRECEIVEDBYPEER, params); + } + + @Override + public void onLocalInvitationAccepted(LocalInvitation localInvitation, String response) { + WritableMap params = Arguments.createMap(); + params.putString("calleeId", localInvitation.getCalleeId()); + params.putString("content", localInvitation.getContent()); + params.putInt("state", localInvitation.getState()); + params.putString("channelId", localInvitation.getChannelId()); + params.putString("response", response); + sendEvent(AgoraRTMConstants.AG_LOCALINVITATIONACCEPTED, params); + } + + @Override + public void onLocalInvitationRefused(LocalInvitation localInvitation, String response) { + WritableMap params = Arguments.createMap(); + params.putString("calleeId", localInvitation.getCalleeId()); + params.putString("content", localInvitation.getContent()); + params.putInt("state", localInvitation.getState()); + params.putString("channelId", localInvitation.getChannelId()); + params.putString("response", response); + sendEvent(AgoraRTMConstants.AG_LOCALINVITATIONREFUSED, params); + } + + @Override + public void onLocalInvitationCanceled(LocalInvitation localInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("calleeId", localInvitation.getCalleeId()); + params.putString("content", localInvitation.getContent()); + params.putInt("state", localInvitation.getState()); + params.putString("channelId", localInvitation.getChannelId()); + params.putString("response", localInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_LOCALINVITATIONCANCELED, params); + } + + @Override + public void onLocalInvitationFailure(LocalInvitation localInvitation, int code) { + WritableMap params = Arguments.createMap(); + params.putString("calleeId", localInvitation.getCalleeId()); + params.putString("content", localInvitation.getContent()); + params.putInt("state", localInvitation.getState()); + params.putString("channelId", localInvitation.getChannelId()); + params.putString("response", localInvitation.getResponse()); + params.putInt("code", code); + sendEvent(AgoraRTMConstants.AG_LOCALINVITATIONFAILURE, params); + } + + @Override + public void onRemoteInvitationReceived(RemoteInvitation remoteInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("callerId", remoteInvitation.getCallerId()); + params.putString("content", remoteInvitation.getContent()); + params.putInt("state", remoteInvitation.getState()); + params.putString("channelId", remoteInvitation.getChannelId()); + params.putString("response", remoteInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_REMOTEINVITATIONRECEIVED, params); + } + + @Override + public void onRemoteInvitationAccepted(RemoteInvitation remoteInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("callerId", remoteInvitation.getCallerId()); + params.putString("content", remoteInvitation.getContent()); + params.putInt("state", remoteInvitation.getState()); + params.putString("channelId", remoteInvitation.getChannelId()); + params.putString("response", remoteInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_REMOTEINVITATIONACCEPTED, params); + } + + @Override + public void onRemoteInvitationRefused(RemoteInvitation remoteInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("callerId", remoteInvitation.getCallerId()); + params.putString("content", remoteInvitation.getContent()); + params.putInt("state", remoteInvitation.getState()); + params.putString("channelId", remoteInvitation.getChannelId()); + params.putString("response", remoteInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_REMOTEINVITATIONREFUSED, params); + } + + @Override + public void onRemoteInvitationCanceled(RemoteInvitation remoteInvitation) { + WritableMap params = Arguments.createMap(); + params.putString("callerId", remoteInvitation.getCallerId()); + params.putString("content", remoteInvitation.getContent()); + params.putInt("state", remoteInvitation.getState()); + params.putString("channelId", remoteInvitation.getChannelId()); + params.putString("response", remoteInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_REMOTEINVITATIONCANCELED, params); + } + + @Override + public void onRemoteInvitationFailure(RemoteInvitation remoteInvitation, int code) { + WritableMap params = Arguments.createMap(); + params.putString("callerId", remoteInvitation.getCallerId()); + params.putString("content", remoteInvitation.getContent()); + params.putInt("state", remoteInvitation.getState()); + params.putString("channelId", remoteInvitation.getChannelId()); + params.putInt("code", code); + params.putString("response", remoteInvitation.getResponse()); + sendEvent(AgoraRTMConstants.AG_REMOTEINVITATIONFAILURE, params); + } + + // RtmChannelListener + // channel message received + @Override + public void onMessageReceived(RtmMessage rtmMessage, RtmChannelMember rtmChannelMember) { + String channelId = rtmChannelMember.getChannelId(); + String ts = Long.toString(rtmMessage.getServerReceivedTs()); + String text = rtmMessage.getText(); + boolean isOffline = rtmMessage.isOfflineMessage(); + WritableMap message = Arguments.createMap(); + String uid = rtmChannelMember.getUserId(); + message.putString("channelId", channelId); + message.putString("uid", uid); + message.putString("text", text); + message.putString("ts", ts); + message.putBoolean("offline", isOffline); + sendEvent(AgoraRTMConstants.AG_CHANNELMESSAGERECEVIED, message); + } + + @Override + public void onMemberJoined(RtmChannelMember rtmChannelMember) { + String channelId = rtmChannelMember.getChannelId(); + String userId = rtmChannelMember.getUserId(); + WritableMap message = Arguments.createMap(); + message.putString("channelId", channelId); + message.putString("uid", userId); + sendEvent(AgoraRTMConstants.AG_CHANNELMEMBERJOINED, message); + } + + @Override + public void onMemberLeft(RtmChannelMember rtmChannelMember) { + String channelId = rtmChannelMember.getChannelId(); + String userId = rtmChannelMember.getUserId(); + WritableMap message = Arguments.createMap(); + message.putString("channelId", channelId); + message.putString("uid", userId); + sendEvent(AgoraRTMConstants.AG_CHANNELMEMBERLEFT, message); + } +} diff --git a/android/src/main/java/io/agora/agora_rtm/AgoraRTMPackage.java b/android/src/main/java/io/agora/agora_rtm/AgoraRTMPackage.java new file mode 100644 index 0000000..c56d304 --- /dev/null +++ b/android/src/main/java/io/agora/agora_rtm/AgoraRTMPackage.java @@ -0,0 +1,31 @@ +package io.agora.agora_rtm; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class AgoraRTMPackage implements ReactPackage { + + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + return Arrays.asList(new NativeModule[]{ + new AgoraRTMModule(reactContext), + }); + } + + public List> createJSModules() { + return Collections.emptyList(); + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Arrays.asList( + ); + } +} diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml new file mode 100644 index 0000000..caf5fca --- /dev/null +++ b/android/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + react-native-agora + diff --git a/ios/AgoraRTM.xcodeproj/project.pbxproj b/ios/AgoraRTM.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d3f2e2e --- /dev/null +++ b/ios/AgoraRTM.xcodeproj/project.pbxproj @@ -0,0 +1,338 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 08F723EE22C393CE00CA9C7C /* AgoraRtmKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08F723ED22C393CE00CA9C7C /* AgoraRtmKit.framework */; }; + 08F723EF22C393F700CA9C7C /* AgoraRTM.h in Sources */ = {isa = PBXBuildFile; fileRef = 084F8C8B229C2D3300481837 /* AgoraRTM.h */; }; + 08F723F022C393F700CA9C7C /* AgoraRTM.m in Sources */ = {isa = PBXBuildFile; fileRef = 084F8C8C229C2D4100481837 /* AgoraRTM.m */; }; + 08F723F122C393F700CA9C7C /* AgoraConst.h in Sources */ = {isa = PBXBuildFile; fileRef = 08F7231422C228C300CA9C7C /* AgoraConst.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 08F723E222C3933900CA9C7C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 084F8C8B229C2D3300481837 /* AgoraRTM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AgoraRTM.h; sourceTree = ""; }; + 084F8C8C229C2D4100481837 /* AgoraRTM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AgoraRTM.m; sourceTree = ""; }; + 08F7231422C228C300CA9C7C /* AgoraConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AgoraConst.h; sourceTree = ""; }; + 08F723E422C3933900CA9C7C /* libAgoraRTM.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAgoraRTM.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 08F723ED22C393CE00CA9C7C /* AgoraRtmKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AgoraRtmKit.framework; path = ../../../Project/AgoraRNQuickStart/ios/Pods/AgoraRtm_iOS/AgoraRtmKit.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 08F723E122C3933900CA9C7C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 08F723EE22C393CE00CA9C7C /* AgoraRtmKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 084F8C86229C2CDE00481837 /* src */ = { + isa = PBXGroup; + children = ( + 084F8C8B229C2D3300481837 /* AgoraRTM.h */, + 084F8C8C229C2D4100481837 /* AgoraRTM.m */, + 08F7231422C228C300CA9C7C /* AgoraConst.h */, + ); + path = src; + sourceTree = ""; + }; + 6E799F31625F4C227FD4A6BF /* Products */ = { + isa = PBXGroup; + children = ( + 08F723E422C3933900CA9C7C /* libAgoraRTM.a */, + ); + name = Products; + sourceTree = ""; + }; + BCBE5B6CFD41E161D60AF5B7 = { + isa = PBXGroup; + children = ( + 084F8C86229C2CDE00481837 /* src */, + 6E799F31625F4C227FD4A6BF /* Products */, + F5336B878807E00A0A8B50D5 /* Frameworks */, + ); + sourceTree = ""; + }; + F5336B878807E00A0A8B50D5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 08F723ED22C393CE00CA9C7C /* AgoraRtmKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 08F723E322C3933900CA9C7C /* AgoraRTM */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08F723EA22C3933A00CA9C7C /* Build configuration list for PBXNativeTarget "AgoraRTM" */; + buildPhases = ( + 08F723E022C3933900CA9C7C /* Sources */, + 08F723E122C3933900CA9C7C /* Frameworks */, + 08F723E222C3933900CA9C7C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AgoraRTM; + productName = AgoraRTMlib; + productReference = 08F723E422C3933900CA9C7C /* libAgoraRTM.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B39EE812DD3705C139FB7715 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1020; + LastUpgradeCheck = 1020; + TargetAttributes = { + 08F723E322C3933900CA9C7C = { + CreatedOnToolsVersion = 10.2.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 8DCDDD596A849FAF6FCA0276 /* Build configuration list for PBXProject "AgoraRTM" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = BCBE5B6CFD41E161D60AF5B7; + productRefGroup = 6E799F31625F4C227FD4A6BF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 08F723E322C3933900CA9C7C /* AgoraRTM */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 08F723E022C3933900CA9C7C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08F723EF22C393F700CA9C7C /* AgoraRTM.h in Sources */, + 08F723F022C393F700CA9C7C /* AgoraRTM.m in Sources */, + 08F723F122C393F700CA9C7C /* AgoraConst.h in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 08F723EB22C3933A00CA9C7C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + /Users/ly/Project/AgoraRNQuickStart/ios/Pods/AgoraRtm_iOS, + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Users/ly/Project/AgoraRNQuickStart/ios/Pods/AgoraRtm_iOS, + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../../ios/Pods/AgoraRtm_iOS", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 08F723EC22C3933A00CA9C7C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + /Users/ly/Project/AgoraRNQuickStart/ios/Pods/AgoraRtm_iOS, + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Users/ly/Project/AgoraRNQuickStart/ios/Pods/AgoraRtm_iOS, + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../../ios/Pods/AgoraRtm_iOS", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 958FD709867C6089CDFE0BAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + FA1D1D6DCBEED960A0CC8CF4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 08F723EA22C3933A00CA9C7C /* Build configuration list for PBXNativeTarget "AgoraRTM" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08F723EB22C3933A00CA9C7C /* Debug */, + 08F723EC22C3933A00CA9C7C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DCDDD596A849FAF6FCA0276 /* Build configuration list for PBXProject "AgoraRTM" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 958FD709867C6089CDFE0BAE /* Debug */, + FA1D1D6DCBEED960A0CC8CF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B39EE812DD3705C139FB7715 /* Project object */; +} diff --git a/ios/src/AgoraConst.h b/ios/src/AgoraConst.h new file mode 100644 index 0000000..189a157 --- /dev/null +++ b/ios/src/AgoraConst.h @@ -0,0 +1,28 @@ +// +// AgoraConst.h +// AgoraRTM +// +// Created by Matrixbirds on 2019/6/25. +// + +#ifndef AgoraConst_h +#define AgoraConst_h + +static NSString *AG_ERROR = @"ag_rtm_error"; +static NSString *AG_CONNECTIONSTATECHANGED = @"ag_rtm_connectionStateChanged"; +static NSString *AG_MESSAGERECEIVED = @"ag_rtm_messageReceived"; +static NSString *AG_LOCALINVITATIONRECEIVEDBYPEER = @"ag_rtm_localInvitationReceivedByPeer"; +static NSString *AG_LOCALINVITATIONACCEPTED = @"ag_rtm_localInvitationAccepted"; +static NSString *AG_LOCALINVITATIONREFUSED = @"ag_rtm_localInvitationRefused"; +static NSString *AG_LOCALINVITATIONCANCELED = @"ag_rtm_localInvitationCanceled"; +static NSString *AG_LOCALINVITATIONFAILURE = @"ag_rtm_localInvitationFailure"; +static NSString *AG_REMOTEINVITATIONFAILURE = @"ag_rtm_remoteInvitationFailure"; +static NSString *AG_REMOTEINVITATIONRECEIVED = @"ag_rtm_remoteInvitationReceived"; +static NSString *AG_REMOTEINVITATIONACCEPTED = @"ag_rtm_remoteInvitationAccepted"; +static NSString *AG_REMOTEINVITATIONREFUSED = @"ag_rtm_remoteInvitationRefused"; +static NSString *AG_REMOTEINVITATIONCANCELED = @"ag_rtm_remoteInvitationCanceled"; +static NSString *AG_CHANNELMESSAGERECEVIED = @"ag_rtm_channelMessageReceived"; +static NSString *AG_CHANNELMEMBERJOINED = @"ag_rtm_channelMemberJoined"; +static NSString *AG_CHANNELMEMBERLEFT = @"ag_rtm_channelMemberLeft"; +static NSString *AG_TOKEN_EXPIRED = @"ag_rtm_tokenExpired"; +#endif /* AgoraConst_h */ diff --git a/ios/src/AgoraRTM.h b/ios/src/AgoraRTM.h new file mode 100644 index 0000000..242f925 --- /dev/null +++ b/ios/src/AgoraRTM.h @@ -0,0 +1,19 @@ +// +// AgoraRTM.h +// AgoraRTM +// +// Created by Matrixbirds on 2019/5/27. +// + +#ifndef AgoraRTM_h +#define AgoraRTM_h + +#import +#import +#import + +@interface AgoraRTM : RCTEventEmitter +- (void) sendEvent:(NSString *)msg params:(NSDictionary *)params; +@end + +#endif /* AgoraRTM_h */ diff --git a/ios/src/AgoraRTM.m b/ios/src/AgoraRTM.m new file mode 100644 index 0000000..910c3fc --- /dev/null +++ b/ios/src/AgoraRTM.m @@ -0,0 +1,590 @@ +// +// AgoraRTM.m +// AgoraRTM +// +// Created by Matrixbirds on 2019/5/27. +// + +#import "AgoraRTM.h" +#import "AgoraConst.h" +#import +#import +#import + +@interface AgoraRTM () +@property (strong, nonatomic) AgoraRtmKit *rtmEngine; +@property (strong, nonatomic) AgoraRtmCallKit *rtmCallManager; +@property (strong, nonatomic) NSMutableDictionary *channels; +@property (strong, nonatomic) NSMutableDictionary *localInvitations; +@property (strong, nonatomic) NSMutableDictionary *remoteInvitations; +@end + +@implementation AgoraRTM { + bool hasListeners; +} + ++(BOOL)requiresMainQueueSetup { + return YES; +} + +RCT_EXPORT_MODULE(); + +- (void) startObserving { + hasListeners = YES; +} + +- (void) stopObserving { + hasListeners = NO; +} + +- (void) sendEvent:(NSString *)msg params:(NSDictionary *)params { + if (hasListeners) { + [self sendEventWithName:msg body:params]; + } +} + +- (NSArray*) supportedEvents { + return @[ + AG_ERROR, + AG_CONNECTIONSTATECHANGED, + AG_MESSAGERECEIVED, + AG_LOCALINVITATIONRECEIVEDBYPEER, + AG_LOCALINVITATIONACCEPTED, + AG_LOCALINVITATIONREFUSED, + AG_LOCALINVITATIONCANCELED, + AG_LOCALINVITATIONFAILURE, + AG_REMOTEINVITATIONFAILURE, + AG_REMOTEINVITATIONRECEIVED, + AG_REMOTEINVITATIONACCEPTED, + AG_REMOTEINVITATIONREFUSED, + AG_REMOTEINVITATIONCANCELED, + AG_CHANNELMESSAGERECEVIED, + AG_CHANNELMEMBERJOINED, + AG_CHANNELMEMBERLEFT, + AG_TOKEN_EXPIRED + ]; +} + +// init AgoraRTM instance, set event observing and init it's internal resources +RCT_EXPORT_METHOD(init:(NSString *)appId) { + self.rtmEngine = [[AgoraRtmKit new] initWithAppId:appId delegate:self]; + self.rtmCallManager = [[self rtmEngine] getRtmCallKit]; + self.channels = [NSMutableDictionary dictionary]; + self.localInvitations = [NSMutableDictionary dictionary]; + self.remoteInvitations = [NSMutableDictionary dictionary]; + [self startObserving]; +} + +// destroy AgoraRTM instance, remove event observing and it's internal resources +RCT_EXPORT_METHOD(destroy) { + [self stopObserving]; + self.rtmCallManager = nil; + self.rtmEngine = nil; + for (id key in self.channels) { + if (self.channels[key] != nil) { + [self.rtmEngine destroyChannelWithId:key]; + } + } + [self.channels removeAllObjects]; + self.channels = nil; + [self.localInvitations removeAllObjects]; + self.localInvitations = nil; + [self.remoteInvitations removeAllObjects]; + self.remoteInvitations = nil; +} + +// login +RCT_EXPORT_METHOD(login:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSString *token = [params objectForKey:@"token"] ? params[@"token"] : nil; + [[self rtmEngine]loginByToken:token + user:params[@"uid"] completion:^(AgoraRtmLoginErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// logout +RCT_EXPORT_METHOD(logout:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [[self rtmEngine] logoutWithCompletion:^(AgoraRtmLogoutErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// renewToken +RCT_EXPORT_METHOD(renewToken:(NSString*)token + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [[self rtmEngine] renewToken:token completion:^(NSString *token, AgoraRtmRenewTokenErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// sendMessageToPeer +RCT_EXPORT_METHOD(sendMessageToPeer:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + AgoraRtmSendMessageOptions *options = [AgoraRtmSendMessageOptions new]; + [options setEnableOfflineMessaging:[params[@"offline"] boolValue]]; + AgoraRtmMessage *msg = [[AgoraRtmMessage new] initWithText:params[@"text"]]; + [[self rtmEngine] sendMessage:msg toPeer:params[@"peerId"] sendMessageOptions:options completion:^(AgoraRtmSendPeerMessageErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// join channel +RCT_EXPORT_METHOD(joinChannel:(NSString *)channelId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + AgoraRtmChannel *rtmChannel = [_rtmEngine createChannelWithId:channelId delegate:self]; + if (nil != rtmChannel){ + [rtmChannel joinWithCompletion:^(AgoraRtmJoinChannelErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + [self.channels setObject:rtmChannel forKey:channelId]; + resolve(nil); + } + }]; + } else { + reject(@"-1", @"channel_create_failed", nil); + } +} + +// leave channel +RCT_EXPORT_METHOD(leaveChannel:(NSString *) channelId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + AgoraRtmChannel *rtmChannel = _channels[channelId]; + if (rtmChannel != nil) { + [rtmChannel leaveWithCompletion:^(AgoraRtmLeaveChannelErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + [self.channels removeObjectForKey:channelId]; + [self.rtmEngine destroyChannelWithId:channelId]; + resolve(nil); + } + }]; + } else { + reject(@"-1", @"channel_not_found", nil); + } +} + +// get channel member by channel id +RCT_EXPORT_METHOD(getChannelMembersBychannelId:(NSString *)uid + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + AgoraRtmChannel *rtmChannel = _channels[uid]; + if (rtmChannel != nil) { + [rtmChannel getMembersWithCompletion:^(NSArray * _Nullable members, AgoraRtmGetMembersErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + NSMutableArray *exportMembers = [NSMutableArray new]; + for(AgoraRtmMember *member in members) { + [exportMembers addObject:@{ + @"uid": member.userId, + @"channelId": member.channelId + }]; + } + resolve(@{ + @"members": exportMembers + }); + } + }]; + } else { + reject(@"-1", @"channel_not_found", nil); + } +} + +// send message by channel id +RCT_EXPORT_METHOD(sendMessageByChannelId:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject){ + AgoraRtmChannel *rtmChannel = self.channels[params[@"channelId"]]; + if (rtmChannel != nil) { + AgoraRtmMessage *message = [[AgoraRtmMessage new] initWithText:params[@"text"]]; + [rtmChannel sendMessage:message completion:^(AgoraRtmSendChannelMessageErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; + } else { + reject(@"-1", @"channel_not_found", nil); + } +} + +// query peer online status with ids +RCT_EXPORT_METHOD(queryPeersOnlineStatus:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSArray *ids = [params[@"ids"] allValues]; + [[self rtmEngine] queryPeersOnlineStatus:ids completion:^(NSArray *peerOnlineStatus, AgoraRtmQueryPeersOnlineErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + NSMutableArray *data = [NSMutableArray new]; + for(AgoraRtmPeerOnlineStatus *item in peerOnlineStatus) { + [data addObject:@{ + @"online": @(item.isOnline), + @"uid": item.peerId, + }]; + } + resolve(@{@"items": data}); + } + }]; +} + +// setup local user attributes +RCT_EXPORT_METHOD(setLocalUserAttributes:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSArray *attributesParam = params[@"attributes"]; + NSMutableArray *attributes = [[NSMutableArray alloc] init]; + for (NSDictionary *attribute in attributesParam) { + AgoraRtmAttribute *attr = [[AgoraRtmAttribute alloc] init]; + attr.key = attribute[@"key"]; + attr.value = attribute[@"value"]; + [attributes addObject:attr]; + } + [_rtmEngine setLocalUserAttributes:attributes completion:^(AgoraRtmProcessAttributeErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// replace local user attributes +RCT_EXPORT_METHOD(replaceLocalUserAttributes:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSArray *attributesParam = params[@"attributes"]; + NSMutableArray *attributes = [[NSMutableArray alloc] init]; + for (NSDictionary *attribute in attributesParam) { + AgoraRtmAttribute *attr = [[AgoraRtmAttribute alloc] init]; + attr.key = attribute[@"key"]; + attr.value = attribute[@"value"]; + [attributes addObject:attr]; + } + [_rtmEngine addOrUpdateLocalUserAttributes:attributes completion:^(AgoraRtmProcessAttributeErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// remove local user attributes by keys +RCT_EXPORT_METHOD(removeLocalUserAttributesByKeys:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSArray *keys = params[@"keys"]; + [_rtmEngine deleteLocalUserAttributesByKeys:keys completion:^(AgoraRtmProcessAttributeErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// remove all local user attributes +RCT_EXPORT_METHOD(removeAllLocalUserAttributes:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [_rtmEngine clearLocalUserAttributesWithCompletion:^(AgoraRtmProcessAttributeErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; +} + +// get local user attributes by uid +RCT_EXPORT_METHOD(getUserAttributesByUid:(NSString *)uid + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [_rtmEngine getUserAllAttributes:uid completion:^(NSArray * _Nullable attributes, NSString *userId, AgoraRtmProcessAttributeErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + NSMutableDictionary *exportAttributes = [NSMutableDictionary dictionary]; + for (AgoraRtmAttribute *item in attributes) { + [exportAttributes setObject:item.value forKey:item.key]; + } + resolve(@{ + @"uid": uid, + @"attributes": exportAttributes + }); + } + }]; +} + +// send local invitation +RCT_EXPORT_METHOD(sendLocalInvitation:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSString *uid = params[@"uid"]; + AgoraRtmLocalInvitation *localInvitation = [[AgoraRtmLocalInvitation new] initWithCalleeId:uid]; + if ([params objectForKey:@"content"]) { + localInvitation.content = params[@"content"]; + } + localInvitation.channelId = params[@"channelId"]; + [_rtmCallManager sendLocalInvitation:localInvitation completion:^(AgoraRtmInvitationApiCallErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + [self.localInvitations setObject:localInvitation forKey:uid]; + resolve(nil); + } + }]; +} + +// cancel local invitation +RCT_EXPORT_METHOD(cancelLocalInvitation:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSString *uid = params[@"uid"]; + AgoraRtmLocalInvitation *localInvitation = _localInvitations[uid]; + if (localInvitation != nil) { + if ([params objectForKey:@"content"]) { + localInvitation.content = params[@"content"]; + } + localInvitation.channelId = params[@"channelId"]; + [_rtmCallManager cancelLocalInvitation:localInvitation completion:^(AgoraRtmInvitationApiCallErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + resolve(nil); + } + }]; + } else { + reject(@"-1", @"local_invitation_not_found", nil); + } +} + +// accept remote invitation +RCT_EXPORT_METHOD(acceptRemoteInvitation:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSString *uid = params[@"uid"]; + AgoraRtmRemoteInvitation *remoteInvitation = self.remoteInvitations[uid]; + if (remoteInvitation != nil) { + if ([params objectForKey:@"response"]) { + remoteInvitation.response = params[@"response"]; + } + [_rtmCallManager acceptRemoteInvitation:remoteInvitation completion:^(AgoraRtmInvitationApiCallErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + [self.remoteInvitations removeObjectForKey:uid]; + resolve(nil); + } + }]; + } else { + reject(@"-1", @"remote_invitation_not_found", nil); + } +} + +// refuse remote invitation +RCT_EXPORT_METHOD(refuseRemoteInvitation:(NSDictionary *)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + NSString *uid = params[@"uid"]; + AgoraRtmRemoteInvitation *remoteInvitation = _remoteInvitations[uid]; + if (nil != remoteInvitation) { + if ([params objectForKey:@"response"]) { + remoteInvitation.response = params[@"response"]; + } + [_rtmCallManager refuseRemoteInvitation:remoteInvitation completion:^(AgoraRtmInvitationApiCallErrorCode errorCode) { + if (0 != (int)errorCode) { + reject(@(-1).stringValue, @(errorCode).stringValue, nil); + } else { + [self.remoteInvitations removeObjectForKey:uid]; + resolve(nil); + } + }]; + } else { + reject(@"-1", @"remote_invitation_not_found", nil); + } +} + +#pragma mark - AgoraRtmDelegate + +- (void)rtmKit:(AgoraRtmKit *_Nonnull)kit connectionStateChanged:(AgoraRtmConnectionState)state reason:(AgoraRtmConnectionChangeReason)reason { + [self sendEvent:AG_CONNECTIONSTATECHANGED params:@{ + @"state": @(state), + @"reason": @(reason) + }]; +} + +- (void)rtmKit:(AgoraRtmKit *_Nonnull)kit messageReceived:(AgoraRtmMessage *_Nonnull)message fromPeer:(NSString *_Nonnull)peerId { + [self sendEvent:AG_MESSAGERECEIVED params:@{ + @"text": message.text, + @"ts": @(message.serverReceivedTs), + @"offline": @(message.isOfflineMessage), + @"peerId": peerId + }]; +} + +- (void)rtmKitTokenDidExpire:(AgoraRtmKit *_Nonnull)kit { + [self sendEvent:AG_TOKEN_EXPIRED params:nil]; +} + +#pragma mark - AgoraRtmChannelDelegate + +- (void)channel:(AgoraRtmChannel *_Nonnull)channel memberJoined:(AgoraRtmMember *_Nonnull)member { + [self sendEvent:AG_CHANNELMEMBERJOINED params:@{ + @"channelId": member.channelId, + @"uid": member.userId + }]; +} + +- (void)channel:(AgoraRtmChannel *_Nonnull)channel memberLeft:(AgoraRtmMember *_Nonnull)member { + [self sendEvent:AG_CHANNELMEMBERLEFT params:@{ + @"channelId": member.channelId, + @"uid": member.userId + }]; +} + +- (void)channel:(AgoraRtmChannel *_Nonnull)channel messageReceived:(AgoraRtmMessage *_Nonnull)message fromMember:(AgoraRtmMember *_Nonnull)member { + [self sendEvent:AG_CHANNELMESSAGERECEVIED params:@{ + @"channelId": member.channelId, + @"uid": member.userId, + @"text": message.text, + @"ts": @(message.serverReceivedTs), + @"offline": @(message.isOfflineMessage) + }]; +} + +#pragma mark - AgoraRtmCallDelegate + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit localInvitationReceivedByPeer:(AgoraRtmLocalInvitation *_Nonnull)localInvitation { + [_localInvitations setObject:localInvitation forKey:localInvitation.calleeId]; + [self sendEvent:AG_LOCALINVITATIONRECEIVEDBYPEER params:@{ + @"calleeId": localInvitation.calleeId, + @"content":localInvitation.content, + @"state": @(localInvitation.state), + @"channelId": localInvitation.channelId, + @"response": localInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit localInvitationAccepted:(AgoraRtmLocalInvitation *_Nonnull)localInvitation withResponse:(NSString *_Nullable)response { + [self sendEvent:AG_LOCALINVITATIONACCEPTED params:@{ + @"calleeId": localInvitation.calleeId, + @"content":localInvitation.content, + @"state": @(localInvitation.state), + @"channelId": localInvitation.channelId, + @"response": localInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit localInvitationRefused:(AgoraRtmLocalInvitation *_Nonnull)localInvitation withResponse:(NSString *_Nullable)response { + [self sendEvent:AG_LOCALINVITATIONREFUSED params:@{ + @"calleeId": localInvitation.calleeId, + @"content":localInvitation.content, + @"state": @(localInvitation.state), + @"channelId": localInvitation.channelId, + @"response": localInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit localInvitationCanceled:(AgoraRtmLocalInvitation *_Nonnull)localInvitation { + [self sendEvent:AG_LOCALINVITATIONCANCELED params:@{ + @"calleeId": localInvitation.calleeId, + @"content":localInvitation.content, + @"state": @(localInvitation.state), + @"channelId": localInvitation.channelId, + @"response": localInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit localInvitationFailure:(AgoraRtmLocalInvitation *_Nonnull)localInvitation errorCode:(AgoraRtmLocalInvitationErrorCode)errorCode { + [self sendEvent:AG_LOCALINVITATIONFAILURE params:@{ + @"calleeId": localInvitation.calleeId, + @"content": localInvitation.content, + @"state": @(localInvitation.state), + @"channelId": localInvitation.channelId, + @"response": localInvitation.response, + @"code": @(errorCode) + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit remoteInvitationReceived:(AgoraRtmRemoteInvitation *_Nonnull)remoteInvitation { + [_remoteInvitations setObject:remoteInvitation forKey:remoteInvitation.callerId]; + [self sendEvent:AG_REMOTEINVITATIONRECEIVED params:@{ + @"callerId": remoteInvitation.callerId, + @"content": remoteInvitation.content, + @"state": @(remoteInvitation.state), + @"channelId": remoteInvitation.channelId, + @"response": remoteInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit remoteInvitationRefused:(AgoraRtmRemoteInvitation *_Nonnull)remoteInvitation { + [self sendEvent:AG_REMOTEINVITATIONREFUSED params:@{ + @"callerId": remoteInvitation.callerId, + @"content": remoteInvitation.content, + @"state": @(remoteInvitation.state), + @"channelId": remoteInvitation.channelId, + @"response": remoteInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit remoteInvitationAccepted:(AgoraRtmRemoteInvitation *_Nonnull)remoteInvitation { + [self sendEvent:AG_REMOTEINVITATIONACCEPTED params:@{ + @"callerId": remoteInvitation.callerId, + @"content": remoteInvitation.content, + @"state": @(remoteInvitation.state), + @"channelId": remoteInvitation.channelId, + @"response": remoteInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit remoteInvitationCanceled:(AgoraRtmRemoteInvitation *_Nonnull)remoteInvitation { + [self sendEvent:AG_REMOTEINVITATIONCANCELED params:@{ + @"callerId": remoteInvitation.callerId, + @"content": remoteInvitation.content, + @"state": @(remoteInvitation.state), + @"channelId": remoteInvitation.channelId, + @"response": remoteInvitation.response + }]; +} + +- (void)rtmCallKit:(AgoraRtmCallKit *_Nonnull)callKit remoteInvitationFailure:(AgoraRtmRemoteInvitation *_Nonnull)remoteInvitation errorCode:(AgoraRtmRemoteInvitationErrorCode)errorCode { + [self sendEvent:AG_REMOTEINVITATIONFAILURE params:@{ + @"callerId": remoteInvitation.callerId, + @"content": remoteInvitation.content, + @"state": @(remoteInvitation.state), + @"channelId": remoteInvitation.channelId, + @"response": remoteInvitation.response, + @"code": @(errorCode) + }]; +} + + +@end diff --git a/package.json b/package.json new file mode 100644 index 0000000..faf9915 --- /dev/null +++ b/package.json @@ -0,0 +1,70 @@ +{ + "name": "agora-react-native-rtm", + "version": "0.9.3", + "description": "React Native around the Agora RTM SDKs for Android and iOS agora", + "summary": "agora native rtm sdk for react-native", + "main": "lib/RtmEngine.js", + "types": "lib/RtmEngine.d.ts", + "scripts": { + "build": "tsc", + "doc": "typedoc --out docs/api ./src" + }, + "repository": { + "type": "git", + "url": "https://github.com/AgoraIO/RN-SDK-RTM.git" + }, + "keywords": [ + "agora", + "react-native", + "rtm", + "realtime", + "agora-react-native-rtm" + ], + "author": "agoraio", + "authors": [ + "https://github.com/agoraio/RN-SDK-RTM/graphs/contributors" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/AgoraIO/RN-SDK-RTM/issues" + }, + "homepage": "https://github.com/AgoraIO/RN-SDK-RTM#README", + "peerDependencies": { + "react": ">= 16.3.1", + "react-native": ">=0.55.0" + }, + "devDependencies": { + "@types/jest": "^23.3.13", + "@types/react": "^16.3.0", + "@types/react-native": "^0.55.0", + "@types/react-test-renderer": "^16.0.3", + "react": "16.3.1", + "react-native": "^0.59.9", + "react-native-typescript-transformer": "^1.2.11", + "ts-jest": "^23.10.5", + "tslint": "^5.12.1", + "typedoc": "^0.14.2", + "typescript": "^3.2.4" + }, + "jest": { + "preset": "react-native", + "moduleFileExtensions": [ + "ts", + "tsx", + "js" + ], + "transform": { + "^.+\\.(js)$": "/node_modules/babel-jest", + "\\.(ts|tsx)$": "/node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", + "testPathIgnorePatterns": [ + "\\.snap$", + "/node_modules/" + ], + "cacheDirectory": ".jest/cache" + }, + "dependencies": { + "tslib": "^1.9.3" + } +} diff --git a/src/RtmEngine.ts b/src/RtmEngine.ts new file mode 100644 index 0000000..63940b1 --- /dev/null +++ b/src/RtmEngine.ts @@ -0,0 +1,320 @@ +import { + RTMEventCallback, + UserInfo, + AgoraPeerMessage, + UserAttribute, + LocalInvitationProps, + RemoteInvitationProps, + Members, + ListPeerStatus, + UserProfile, + RtmEngineEvents +} from './types.d'; + +import { + NativeModules, + NativeEventEmitter, +} from 'react-native'; + +const { AgoraRTM } = NativeModules; + +/** + * `RtmEngine` is the entry point of the react native agora rtm sdk. You can call the {@link createClient} method of {@link RtmEngine} to create an `RtmEngine` instance. + * @noInheritDoc + */ +export default class RtmEngine { + + // internal event identifiy for RtmEngine + private static readonly AG_RTMCHANNEL = "ag_rtm_"; + + // this property is only for dispatch event in RtmEngine instance. + private readonly events: NativeEventEmitter + + // create RtmEngine instance + constructor () { + this.events = new NativeEventEmitter(AgoraRTM); + } + + /** + * supports platform: ios, android + * @events {@link RtmEngineEvents} + * @param evtName string required + * @param callback (evt) => {} required + */ + on(eventName: EventName, callback: RTMEventCallback) { + this.events.addListener(`${RtmEngine.AG_RTMCHANNEL}${eventName}`, callback); + } + + /** + * supports platform: ios, android + * This method creates AgoraRTM instance and begin event observing, collect all both remote and local invitaitons and channels resources. + * method: createClient + * @param appId String + * @return void + */ + createClient(appId: string): void { + return AgoraRTM.init(appId); + } + + /** + * supports platform: ios, android + * This method destroy AgoraRTM instance, stop event observing, release all both remote and local invitaitons and channels resources. + * @param void + * @return void + */ + destroyClient(): void { + return AgoraRTM.destroy(); + } + + /** + * supports platform: ios, android + * This method do login with UserInfo + * @param params {@link UserInfo} + * --- + * token | string | optional | + * uid | string | required | + * --- + * @return Promise + */ + login(params: UserInfo): Promise { + return AgoraRTM.login(params); + } + + /** + * supports platform: ios, android + * This method do logout. + * @return Promise + */ + logout(): Promise { + return AgoraRTM.logout(); + } + + /** + * supports platform: ios, android + * This method do renewToken when got `tokenExpired` event. + * @param token String + * @return Promise + */ + renewToken(token: String): Promise { + return AgoraRTM.renewToken(token); + } + + /** + * supports platform: ios, android + * This method do send p2p message with {@link AgoraPeerMessage} + * @param params AgoraPeerMessage + * --- + * peerId | string | required | + * offline | string | requried | + * text | string | required | + * --- + * @return Promise + */ + sendMessageToPeer(params: AgoraPeerMessage): Promise { + return AgoraRTM.sendMessageToPeer(params); + } + + /** + * supports platform: ios, android + * This method do join channel with channelId + * @param channelId string + * @return Promise + */ + joinChannel(channelId: string): Promise { + return AgoraRTM.joinChannel(channelId); + } + + /** + * supports platform: ios, android + * This method do leave channel with channelId + * @param channelId string + * @return Promise + */ + leaveChannel(channelId: string): Promise { + return AgoraRTM.leaveChannel(channelId); + } + + /** + * supports platform: ios, android + * This method enables you get members by channel id. + * @param channelId string. + * @return Promise {@link Members}} + * + * --- + * members | {@link MemberInfo} | + * --- + * + * MemberInfo + * --- + * uid | string | user id| + * channelId | string | channel id| + * --- + */ + getChannelMembersBychannelId(channelId: string): Promise { + return AgoraRTM.getChannelMembersBychannelId(channelId); + } + + /** + * supports platform: ios, android + * This method enables send message by channel id. + * NOTICE: text bytelength has MAX_SIZE 32kb limit. + * @param channelId string. + * @param text string (bytesize shouldn't >= 32kb) + * @return Promise + */ + sendMessageByChannelId(channelId: string, text: string): Promise { + return AgoraRTM.sendMessageByChannelId({channelId, text}); + } + + /** + * supports platform: ios, android + * This method enables query peer online user by id array. + * @param ids string array + * @return Promise {@link ListPeerStatus} + * --- + * items | {@link MemberStatus} | + * --- + * + * MemberStatus + * --- + * uid | string | user id| + * online | boolean | online state| + * --- + */ + queryPeersOnlineStatus(ids: string []): Promise { + return AgoraRTM.queryPeersOnlineStatus({ids}); + } + + /** + * supports platform: ios, android + * This method enables set local user attributes with attributes {@link UserAttribute} + * @param attributes {@link UserAttribute []} + * @return Promise + * + * UserAttribute + * --- + * key | string | required | + * value | string | required | + * --- + */ + setLocalUserAttributes(attributes: UserAttribute[]): Promise { + return AgoraRTM.setLocalUserAttributes({attributes}); + } + + /** + * supports platform: ios, android + * This method enables you to replace attribute already exists or add attribute wasn't set for local user attributes; + * @param attributes {@link UserAttribute []} + * @return Promise + */ + replaceLocalUserAttributes(attributes: UserAttribute[]): Promise { + return AgoraRTM.replaceLocalUserAttributes({attributes}); + } + + /** + * supports platform: ios, android + * This method enables you to remove exists attribute for local user. + * @param keys string [] + * @return Promise + */ + removeLocalUserAttributesByKeys(keys: string[]): Promise { + return AgoraRTM.removeLocalUserAttributesByKeys({keys}); + } + + /** + * supports platform: ios, android + * This method enables you to remove all of local user attributes; + * @param void + * @return Promise + */ + removeAllLocalUserAttributes(): Promise { + return AgoraRTM.removeAllLocalUserAttributes(); + } + + /** + * supports platform: ios, android + * This method enables you get user attributes by uid. + * @param uid string. user id + * @return Promise {@link UserProfile} + */ + getUserAttributesByUid(uid: string): Promise { + return AgoraRTM.getUserAttributesByUid(uid); + } + + /** + * supports platform: ios, android + * This method enables send local invitation with invitationProps. + * NOTICE: content bytelength has MAX_SIZE 32kb limit. + * @param invitationProps {@link LocalInvitationProps} + * + * LocalInvitationProps + * --- + * uid | string | required | + * channelId | string | required | + * content | string | optional | 32kb limit | + * --- + * + * @return Promise + */ + sendLocalInvitation(invitationProps: LocalInvitationProps): Promise { + return AgoraRTM.sendLocalInvitation(invitationProps); + } + + /** + * supports platform: ios, android + * This method enables cancel local invitation with invitationProps. + * NOTICE: content bytelength has MAX_SIZE 32kb limit. + * @param invitationProps {@link LocalInvitationProps} + * + * LocalInvitationProps + * --- + * uid | string | required | + * channelId | string | required | + * content | string | optional | 32kb limit | + * --- + * + * @return Promies + */ + cancelLocalInvitation(invitationProps: LocalInvitationProps): Promise { + return AgoraRTM.cancelLocalInvitation(invitationProps); + } + + /** + * supports platform: ios, android + * This method enables accept remote invitation with RemoteInvitationProps. + * NOTICE: content bytelength has MAX_SIZE 32kb limit. + * @param invitationProps {@link RemoteInvitationProps} + * + * RemoteInvitationProps + * --- + * uid | string | required | + * channelId | string | required | + * response | string | optional | 32kb limit | + * --- + * + * @return Promise + */ + acceptRemoteInvitation(remoteInvitationProps: RemoteInvitationProps): Promise { + return AgoraRTM.sendRemoteInvitation(remoteInvitationProps); + } + + /** + * supports platform: ios, android + * This method enables refuse remote invitation with RemoteInvitationProps. + * NOTICE: content bytelength has MAX_SIZE 32kb limit. + * @param invitationProps {@link RemoteInvitationProps} + * + * RemoteInvitationProps + * --- + * uid | string | required | + * channelId | string | required | + * response | string | optional | 32kb limit | + * --- + * + * @return Promise + */ + refuseRemoteInvitation(remoteInvitationProps: RemoteInvitationProps): Promise { + return AgoraRTM.refuseRemoteInvitation(remoteInvitationProps); + } + +}; \ No newline at end of file diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..bbdd958 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,247 @@ +import { Text } from "react-native"; + +export interface AgoraRTMClientOptions { + appID: string + token?: string + logined?: boolean + connectionState: string +} + +export interface AgoraMessage { + text: string + channelId?: string + peerId?: string +} + +export type RTMEventCallback = () => {} + +export interface UserInfo { + token?: string + uid: String +} + +export interface AgoraPeerMessage { + peerId: string + offline: boolean + text: string +} + +export interface UserAttribute { + key: string + value: string +} + +export interface LocalInvitationProps { + uid: string + content?: string + channelId: string +} + +export interface RemoteInvitationProps { + uid: string + response?: string + channelId: string +} + +export interface MemberInfo { + uid: string + channelId: string +} + +export interface Members { + members: MemberInfo[] +} + +export interface MemberStatus { + uid: string + online: boolean +} + +export interface ListPeerStatus { + items: MemberStatus[] +} + +export interface UserProfile { + uid: string + attributes: any +} + +export interface ConnectionState { + state: number + reason: number +} + +export interface RTMPeerMessage { + text: string // received text + ts: string // received time + offline: boolean // offline state + peerId: string // peer id +} + +export interface RTMChannelMessage { + channelId: string // channel id + uid: string // sender uid + text: string // text + ts: string // time string + offline: boolean // offline state +} + +export interface RTMLocalInvitationMessage { + calleeId: string // callee id + content: string // content + state: number // state + channelId: string // channel id + response: string // response +} + +export interface RTMLocalInvitationErrorMessage { + calleeId: string // callee id + content: string // content + state: number // state + channelId: string // channel id + response: string // response + code: number // error code +} + +export interface RTMRemoteInvitationMessage { + callerId: string // caller id + content: string // content + state: number // state + channelId: string // channel id + response: string // response +} + +export interface RTMRemoteInvitationErrorMessage { + callerId: string // caller id + content: string // content + state: number // state + channelId: string // channel id + response: string // response + code: number // error code +} + +export interface RTMMemberInfo { + channelId: string + uid: string +} + +export interface RtmEngineEvents { + /** + * @event + * @param evt The received event object + * error | occurs when wrapper emit error | on("error") | + */ + error: (evt: any) => void + + /** + * @event + * @param evt The received event object {@link ConnectionState} + * connectionStateChanged | occurs when connection state changed | + */ + connectionStateChanged: (evt: ConnectionState) => void + + /** + * @event + * @param evt The received event object {@link RTMPeerMessage} + * messageReceived | occurs when message received | + */ + messageReceived: (evt: any) => void + + /** + * @event + * @param evt The received event object {@link RTMLocalInvitationMessage} + * localInvitationReceivedByPeer | occurs when local inviation received by peer | + */ + localInvitationReceivedByPeer: (evt: RTMLocalInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMLocalInvitationMessage} + * localInvitationAccepted | occurs when local invitation accepted | + */ + localInvitationAccepted: (evt: RTMLocalInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMLocalInvitationMessage} + * localInvitationRefused | occurs when local invitation refused | + */ + localInvitationRefused: (evt: RTMLocalInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMLocalInvitationMessage} + * localInvitationCanceled | occurs when local invitation canceled | + */ + localInvitationCanceled: (evt: RTMLocalInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMLocalInvitationErrorMessage} + * localInvitationFailure | occurs when local invitation failure | + */ + localInvitationFailure: (evt: RTMLocalInvitationErrorMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMRemoteInvitationErrorMessage} + * remoteInvitationFailure | occurs when remote invitation failure | + */ + remoteInvitationFailure: (evt: RTMRemoteInvitationErrorMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMRemoteInvitationMessage} + * remoteInvitationReceived | occurs when remote invitation received | + */ + remoteInvitationReceived: (evt: RTMRemoteInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMRemoteInvitationMessage} + * remoteInvitationAccepted | occurs when remote invitation accepted | + */ + remoteInvitationAccepted: (evt: RTMRemoteInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMRemoteInvitationMessage} + * remoteInvitationRefused | occurs when remote invitation refused | + */ + remoteInvitationRefused: (evt: RTMRemoteInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMRemoteInvitationMessage} + * remoteInvitationCanceled | occurs when remote invitation canceled | + */ + remoteInvitationCanceled: (evt: RTMRemoteInvitationMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMChannelMessage} + * channelMessageReceived | occurs when received channel message | + */ + channelMessageReceived: (evt: RTMChannelMessage) => void + + /** + * @event + * @param evt The received event object {@link RTMMemberInfo} + * channelMemberJoined | occurs when some one joined in the subscribed channel + */ + channelMemberJoined: (evt: RTMMemberInfo) => void + + /** + * @event + * @param evt The received event object {@link RTMMemberInfo} + * channelMemberLeft | occurs when sone one left from u subscribed channel + */ + channelMemberLeft: (evt: RTMMemberInfo) => void + + /** + * @event + * @param evt The received event object + * tokenExpired | occurs when token has expired | + */ + tokenExpired: (evt: any) => void +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1a8367d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationDir": "./lib", + "types": [ + "react", + "react-native" + ], + "target": "es2015", + "jsx": "react", + "moduleResolution": "node", + "sourceMap": true, + "importHelpers": true, + "outDir": "./lib", + "module": "commonjs", + "strict": true, + "declaration": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "lib": ["es2015"] + }, + "compileOnSave": true, + "include": [ + "src" + ] +} diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..a62ef00 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,4 @@ +{ + "mode": "file", + "out": "docs/api" +}