Merge pull request #481 from rmrs/omer_links

Dynamic Links Support
This commit is contained in:
chrisbianca 2017-10-13 18:27:53 +01:00 committed by GitHub
commit 101a426307
24 changed files with 1532 additions and 802 deletions

View File

@ -90,4 +90,5 @@ dependencies {
compile "com.google.firebase:firebase-perf:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$firebaseVersion"
}

View File

@ -0,0 +1,284 @@
package io.invertase.firebase.links;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.Map;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.LifecycleEventListener;
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.ReadableMap;
import com.google.firebase.dynamiclinks.DynamicLink;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import com.google.firebase.dynamiclinks.ShortDynamicLink;
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.OnFailureListener;
import io.invertase.firebase.Utils;
public class RNFirebaseLinks extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private final static String TAG = RNFirebaseLinks.class.getCanonicalName();
private String mInitialLink = null;
private boolean mInitialLinkInitialized = false;
private interface ResolveHandler {
void onResolved(String url);
}
private interface ErrorHandler {
void onError(Exception e);
}
public RNFirebaseLinks(ReactApplicationContext reactContext) {
super(reactContext);
getReactApplicationContext().addActivityEventListener(this);
getReactApplicationContext().addLifecycleEventListener(this);
}
@Override
public String getName() {
return "RNFirebaseLinks";
}
private void resolveLink(Intent intent, final ResolveHandler resolveHandler, final ErrorHandler errorHandler) {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null) {
Uri deepLinkUri = pendingDynamicLinkData.getLink();
String url = deepLinkUri.toString();
resolveHandler.onResolved(url);
} else {
resolveHandler.onResolved(null);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
errorHandler.onError(e);
}
});
}
@ReactMethod
public void getInitialLink(final Promise promise) {
if (mInitialLinkInitialized) {
promise.resolve(mInitialLink);
} else {
Activity activity = getCurrentActivity();
if (activity != null) {
resolveLink(activity.getIntent(), new ResolveHandler() {
@Override
public void onResolved(String url) {
if (url != null) {
mInitialLink = url;
Log.d(TAG, "getInitialLink received a new dynamic link from pendingDynamicLinkData");
}
Log.d(TAG, "initial link is: " + mInitialLink);
promise.resolve(mInitialLink);
mInitialLinkInitialized = true;
}
}, new ErrorHandler() {
@Override
public void onError(Exception e) {
Log.e(TAG, "getInitialLink: failed to resolve link", e);
promise.reject("links/getDynamicLink", e.getMessage(), e);
}
});
} else {
Log.d(TAG, "getInitialLink: activity is null");
promise.resolve(null);
}
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// Not required for this module
}
@Override
public void onNewIntent(Intent intent) {
resolveLink(intent, new ResolveHandler() {
@Override
public void onResolved(String url) {
if (url != null) {
Log.d(TAG, "handleLink: sending link: " + url);
Utils.sendEvent(getReactApplicationContext(), "dynamic_link_received", url);
}
}
}, new ErrorHandler() {
@Override
public void onError(Exception e) {
Log.e(TAG, "handleLink: failed to resolve link", e);
}
});
}
@Override
public void onHostResume() {
// Not required for this module
}
@Override
public void onHostPause() {
// Not required for this module
}
@Override
public void onHostDestroy() {
mInitialLink = null;
mInitialLinkInitialized = false;
}
@ReactMethod
public void createDynamicLink(final ReadableMap parameters, final Promise promise) {
try {
Map<String, Object> metaData = Utils.recursivelyDeconstructReadableMap(parameters);
DynamicLink.Builder builder = getDynamicLinkBuilderFromMap(metaData);
Uri link = builder.buildDynamicLink().getUri();
Log.d(TAG, "created dynamic link: " + link.toString());
promise.resolve(link.toString());
} catch (Exception ex) {
Log.e(TAG, "create dynamic link failure " + ex.getMessage());
promise.reject("links/failure", ex.getMessage(), ex);
}
}
@ReactMethod
public void createShortDynamicLink(final ReadableMap parameters, final Promise promise) {
try {
Map<String, Object> metaData = Utils.recursivelyDeconstructReadableMap(parameters);
DynamicLink.Builder builder = getDynamicLinkBuilderFromMap(metaData);
Task<ShortDynamicLink> shortLinkTask = getShortDynamicLinkTask(builder, metaData)
.addOnCompleteListener(new OnCompleteListener<ShortDynamicLink>() {
@Override
public void onComplete(@NonNull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
Uri shortLink = task.getResult().getShortLink();
Log.d(TAG, "created short dynamic link: " + shortLink.toString());
promise.resolve(shortLink.toString());
} else {
Log.e(TAG, "create short dynamic link failure " + task.getException().getMessage());
promise.reject("links/failure", task.getException().getMessage(), task.getException());
}
}
});
} catch (Exception ex) {
Log.e(TAG, "create short dynamic link failure " + ex.getMessage());
promise.reject("links/failure", ex.getMessage(), ex);
}
}
private DynamicLink.Builder getDynamicLinkBuilderFromMap(final Map<String, Object> metaData) {
DynamicLink.Builder parametersBuilder = FirebaseDynamicLinks.getInstance().createDynamicLink();
try {
parametersBuilder.setLink(Uri.parse((String) metaData.get("link")));
parametersBuilder.setDynamicLinkDomain((String) metaData.get("dynamicLinkDomain"));
setAndroidParameters(metaData, parametersBuilder);
setIosParameters(metaData, parametersBuilder);
setSocialMetaTagParameters(metaData, parametersBuilder);
} catch (Exception e) {
Log.e(TAG, "error while building parameters " + e.getMessage());
throw e;
}
return parametersBuilder;
}
private Task<ShortDynamicLink> getShortDynamicLinkTask(final DynamicLink.Builder builder, final Map<String, Object> metaData) {
Map<String, Object> suffix = (Map<String, Object>) metaData.get("suffix");
if (suffix != null) {
String option = (String) suffix.get("option");
if ("SHORT".equals(option)) {
return builder.buildShortDynamicLink(ShortDynamicLink.Suffix.SHORT);
} else if ("UNGUESSABLE".equals(option)) {
return builder.buildShortDynamicLink(ShortDynamicLink.Suffix.UNGUESSABLE);
}
}
return builder.buildShortDynamicLink();
}
private void setAndroidParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
Map<String, Object> androidParameters = (Map<String, Object>) metaData.get("androidInfo");
if (androidParameters != null) {
DynamicLink.AndroidParameters.Builder androidParametersBuilder =
new DynamicLink.AndroidParameters.Builder((String) androidParameters.get("androidPackageName"));
if (androidParameters.containsKey("androidFallbackLink")) {
androidParametersBuilder.setFallbackUrl(Uri.parse((String) androidParameters.get("androidFallbackLink")));
}
if (androidParameters.containsKey("androidMinPackageVersionCode")) {
androidParametersBuilder.setMinimumVersion(Integer.parseInt((String) androidParameters.get("androidMinPackageVersionCode")));
}
parametersBuilder.setAndroidParameters(androidParametersBuilder.build());
}
}
private void setIosParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
Map<String, Object> iosParameters = (Map<String, Object>) metaData.get("iosInfo");
if (iosParameters != null) {
DynamicLink.IosParameters.Builder iosParametersBuilder =
new DynamicLink.IosParameters.Builder((String) iosParameters.get("iosBundleId"));
if (iosParameters.containsKey("iosAppStoreId")) {
iosParametersBuilder.setAppStoreId((String) iosParameters.get("iosAppStoreId"));
}
if (iosParameters.containsKey("iosCustomScheme")) {
iosParametersBuilder.setCustomScheme((String) iosParameters.get("iosCustomScheme"));
}
if (iosParameters.containsKey("iosFallbackLink")) {
iosParametersBuilder.setFallbackUrl(Uri.parse((String) iosParameters.get("iosFallbackLink")));
}
if (iosParameters.containsKey("iosIpadBundleId")) {
iosParametersBuilder.setIpadBundleId((String) iosParameters.get("iosIpadBundleId"));
}
if (iosParameters.containsKey("iosIpadFallbackLink")) {
iosParametersBuilder.setIpadFallbackUrl(Uri.parse((String) iosParameters.get("iosIpadFallbackLink")));
}
if (iosParameters.containsKey("iosMinPackageVersionCode")) {
iosParametersBuilder.setMinimumVersion((String) iosParameters.get("iosMinPackageVersionCode"));
}
parametersBuilder.setIosParameters(iosParametersBuilder.build());
}
}
private void setSocialMetaTagParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
Map<String, Object> socialMetaTagParameters = (Map<String, Object>) metaData.get("socialMetaTagInfo");
if (socialMetaTagParameters != null) {
DynamicLink.SocialMetaTagParameters.Builder socialMetaTagParametersBuilder =
new DynamicLink.SocialMetaTagParameters.Builder();
if (socialMetaTagParameters.containsKey("socialDescription")) {
socialMetaTagParametersBuilder.setDescription((String) socialMetaTagParameters.get("socialDescription"));
}
if (socialMetaTagParameters.containsKey("socialImageLink")) {
socialMetaTagParametersBuilder.setImageUrl(Uri.parse((String) socialMetaTagParameters.get("socialImageLink")));
}
if (socialMetaTagParameters.containsKey("socialTitle")) {
socialMetaTagParametersBuilder.setTitle((String) socialMetaTagParameters.get("socialTitle"));
}
parametersBuilder.setSocialMetaTagParameters(socialMetaTagParametersBuilder.build());
}
}
}

View File

@ -0,0 +1,39 @@
package io.invertase.firebase.links;
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.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("unused")
public class RNFirebaseLinksPackage implements ReactPackage {
public RNFirebaseLinksPackage() {
}
/**
* @param reactContext react application context that can be used to create modules
* @return list of native modules to register with the newly created catalyst instance
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new RNFirebaseLinks(reactContext));
return modules;
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@ -41,7 +41,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ |
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | | ❌ |
| **Dynamic Links** | ❌ | ❌ | | ❌ |
| **Firestore** | ❌ | ❌ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ | ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |

View File

@ -25,6 +25,7 @@
- [Cloud Messaging](/modules/cloud-messaging)
- [Crash Reporting](/modules/crash)
- [Database](/modules/database)
- [Dynamic Links](/modules/links)
- [Firestore (Beta)](/modules/firestore)
- [Remote Config](/modules/config)
- [Storage](/modules/storage)

View File

@ -49,6 +49,7 @@ dependencies {
compile "com.google.firebase:firebase-crash:11.4.2"
compile "com.google.firebase:firebase-database:11.4.2"
compile "com.google.firebase:firebase-firestore:11.4.2"
compile "com.google.firebase:firebase-invites:11.4.2"
compile "com.google.firebase:firebase-messaging:11.4.2"
compile "com.google.firebase:firebase-perf:11.4.2"
compile "com.google.firebase:firebase-storage:11.4.2"
@ -97,6 +98,7 @@ import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage; // Firebase R
import io.invertase.firebase.crash.RNFirebaseCrashPackage; // Firebase Crash Reporting
import io.invertase.firebase.database.RNFirebaseDatabasePackage; // Firebase Realtime Database
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage; // Firebase Firestore
import io.invertase.firebase.links.RNFirebaseLinksPackage; // Firebase Links
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; // Firebase Cloud Messaging
import io.invertase.firebase.perf.RNFirebasePerformancePackage; // Firebase Performance
import io.invertase.firebase.storage.RNFirebaseStoragePackage; // Firebase Storage
@ -117,6 +119,7 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseCrashPackage(),
new RNFirebaseDatabasePackage(),
new RNFirebaseFirestorePackage(),
new RNFirebaseLinksPackage(),
new RNFirebaseMessagingPackage(),
new RNFirebasePerformancePackage(),
new RNFirebaseStoragePackage()
@ -206,3 +209,22 @@ dependencies {
compile "com.google.firebase:firebase-perf:11.4.2"
}
```
## 7) Dynamic Links (optional)
If you plan on using [Firebase Dynamic
Links](https://firebase.google.com/docs/dynamic-links/):
Make sure to setup dynamic links for Android as described [here](https://firebase.google.com/docs/dynamic-links/android/receive#set-up-firebase-and-the-dynamic-links-sdk)
In `android/app/src/main/AndroidManifest.xml`, add a new intent filter to the activity that handles deep links for your app (for react-native this is usually MainActivity), and specify the host and the scheme:
```xml
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="yoursite.example.com" android:scheme="http"/>
<data android:host="yoursite.example.com" android:scheme="https"/>
</intent-filter>
```

View File

@ -168,3 +168,72 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
If you're having problems with messages not being received, check out the following blog post for help:
https://firebase.googleblog.com/2017/01/debugging-firebase-cloud-messaging-on.html
## 5) Dynamic Links (optional)
If you plan on using [Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links/) then, you need to:
### 5.1) Setup
Make sure to setup dynamic links for iOS as described [here](https://firebase.google.com/docs/dynamic-links/ios/receive#set-up-firebase-and-the-dynamic-links-sdk)
### 5.2) create a new URL type
In the Info tab of your app's Xcode project, create a new URL type to be used for Dynamic Links. Set the Identifier field to a unique value and the URL scheme field to either your bundle identifier or a unique value.
### 5.3) Enable Associated Domains capability
In the Capabilities tab of your app's Xcode project, enable Associated Domains and
add the following to the Associated Domains list:
`applinks:app_code.app.goo.gl` where `app_code` is your dynamic links domain application code.
### 5.4) Update `AppDelegate.m`
Add the following import:
`#import "RNFirebaseLinks.h"`
Add the following to the `didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` method before `[FIRApp Configure]`:
`[FIROptions defaultOptions].deepLinkURLScheme = CUSTOM_URL_SCHEME;`
where `CUSTOM_URL_SCHEME` is the custom URL scheme you defined in your Xcode project.
In the application:openURL:sourceApplication:annotation: (for iOS 8 and older) add the following:
```objectivec
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
return [RNFirebaseLinks application:application
openURL:url
options:options];
}
```
In the application:openURL:options: (for iOS 9) add the following:
```objectivec
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [RNFirebaseLinks application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
```
In the application:continueUserActivity:restorationHandler (Universal Links on iOS 9 and newer) add the following:
```objectivec
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler {
return [RNFirebaseLinks application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
```

123
docs/modules/links.md Normal file
View File

@ -0,0 +1,123 @@
# Dynamic Links
[Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links/) allows you to create and receive links on both Android and iOS platforms. Assuming the installation instructions have been followed, Firebase Dynamic Links is ready to go.
All Dynamic Links operations are accessed via `firebase.links()`
## Create Dynamic Links
RNFirebase mimics [Firebase's REST API](https://firebase.google.com/docs/dynamic-links/rest) for Dynamic Links creation.
The differences from the REST API are:
1. The input for the methods is a javascript object instead of a JSON object.
2. The response contains the URL string only.
3. There is no `dynamicLinkInfo` element. Instead, all of the elements under it were moved to be under the top-level.
### Methods
#### `createDynamicLink(parameters: Object): Promise<String>`
Creates a long dynamic link.
```javascript
firebase.links().createDynamicLink({
dynamicLinkDomain: "abc123.app.goo.gl",
link: "https://example.com?param1=foo&param2=bar",
androidInfo: {
androidPackageName: "com.example.android"
},
iosInfo: {
iosBundleId: "com.example.ios"
}
}).
then((url) => {
// ...
});
```
#### `createShortDynamicLink(parameters: Object): Promise<String>`
Creates a short dynamic link.
```javascript
firebase.links().createShortDynamicLink({
dynamicLinkDomain: "abc123.app.goo.gl",
link: "https://example.com?param1=foo&param2=bar",
androidInfo: {
androidPackageName: "com.example.android"
},
iosInfo: {
iosBundleId: "com.example.ios"
}
}).
then((url) => {
// ...
});
```
### Parameters
Only the following parameters are currently supported:
```javascript
{
dynamicLinkDomain: 'string',
link: 'string',
androidInfo: {
androidPackageName: 'string',
androidFallbackLink: 'string',
androidMinPackageVersionCode: 'string',
androidLink: 'string',
},
iosInfo: {
iosBundleId: 'string',
iosFallbackLink: 'string',
iosCustomScheme: 'string',
iosIpadFallbackLink: 'string',
iosIpadBundleId: 'string',
iosAppStoreId: 'string',
},
socialMetaTagInfo: {
socialTitle: 'string',
socialDescription: 'string',
socialImageLink: 'string',
},
suffix: {
option: 'string',
},
}
```
**please note:**
1. dynamicLinkDomain and link are mandatory fields. In addition, when using `androidInfo` or `iosInfo`, `androidPackageName` and `iosBundleId` are mandatory (respectively).
2. In oppose to the REST API, There is no `dynamicLinkInfo` element. Instead, all of the elements under it were moved to be under the top-level.
For more information [see reference](https://firebase.google.com/docs/reference/dynamic-links/link-shortener)
## Receive Dynamic Links
### Methods
#### `getInitialLink(): Promise<String>`
Call getInitialLink to access the URL that the app has been launched from. If the app was not launched from a URL, the return value is null.
```javascript
firebase.links().getInitialLink().then((url) => {
//...
});
```
#### `onLink(listener: Function<String>): Function`
On a new URL, the payload URL is passed to the listener callback. This method is only triggered when the app is running. Use getInitialLink for URLs which cause the app to open.
In order to subscribe to the listener, call to the method with a callback and save the returned function.
When you want to unsubscribe, just call the function that returned at subscription.
```javascript
// Subscribe
const unsubscribe = firebase.links().onLink((url) => {
//...
});
// Unsubscribe
unsubscribe();
```

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */; };
8300A7AE1F31E143001B16AB /* RNFirebaseDatabaseReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */; };
8323CF061F6FBD870071420B /* BannerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CEFF1F6FBD870071420B /* BannerComponent.m */; };
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
@ -43,6 +44,8 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
17AF4F691F59CDBF00C02336 /* RNFirebaseLinks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseLinks.h; sourceTree = "<group>"; };
17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseLinks.m; sourceTree = "<group>"; };
8300A7AC1F31E143001B16AB /* RNFirebaseDatabaseReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseDatabaseReference.h; sourceTree = "<group>"; };
8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseDatabaseReference.m; sourceTree = "<group>"; };
8323CEFE1F6FBD870071420B /* BannerComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BannerComponent.h; sourceTree = "<group>"; };
@ -105,9 +108,20 @@
name = Products;
sourceTree = "<group>";
};
17AF4F681F59CDBF00C02336 /* links */ = {
isa = PBXGroup;
children = (
17AF4F691F59CDBF00C02336 /* RNFirebaseLinks.h */,
17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */,
);
name = links;
path = RNFirebase/links;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
17AF4F681F59CDBF00C02336 /* links */,
839D914D1EF3E20A0077C7C8 /* admob */,
839D91541EF3E20A0077C7C8 /* analytics */,
839D91571EF3E20A0077C7C8 /* auth */,
@ -117,11 +131,11 @@
8376F70D1F7C141500D45A85 /* firestore */,
839D91631EF3E20A0077C7C8 /* messaging */,
839D91661EF3E20A0077C7C8 /* perf */,
839D91691EF3E20A0077C7C8 /* storage */,
134814211AA4EA7D00B7C361 /* Products */,
D950369C1D19C77400F7094D /* RNFirebase.h */,
D950369D1D19C77400F7094D /* RNFirebase.m */,
839D91771EF3E22F0077C7C8 /* RNFirebaseEvents.h */,
134814211AA4EA7D00B7C361 /* Products */,
839D91691EF3E20A0077C7C8 /* storage */,
);
sourceTree = "<group>";
};
@ -301,6 +315,7 @@
files = (
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */,
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */,
839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */,
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */,

View File

@ -39,4 +39,7 @@ static NSString *const MESSAGING_NOTIFICATION_RECEIVED = @"messaging_notificatio
static NSString *const ADMOB_INTERSTITIAL_EVENT = @"interstitial_event";
static NSString *const ADMOB_REWARDED_VIDEO_EVENT = @"rewarded_video_event";
// Links
static NSString *const LINKS_DYNAMIC_LINK_RECEIVED = @"dynamic_link_received";
#endif

View File

@ -0,0 +1,33 @@
#ifndef RNFirebaseLinks_h
#define RNFirebaseLinks_h
#import <Foundation/Foundation.h>
#if __has_include(<FirebaseDynamicLinks/FIRDynamicLinks.h>)
#import <Firebase.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RNFirebaseLinks : RCTEventEmitter<RCTBridgeModule> {
}
+ (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation;
+ (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler;
@end
#else
@interface RNFirebaseLinks : NSObject
@end
#endif
#endif

View File

@ -0,0 +1,276 @@
#import "RNFirebaseLinks.h"
#if __has_include(<FirebaseDynamicLinks/FIRDynamicLink.h>)
#import "RNFirebaseEvents.h"
static void sendDynamicLink(NSURL *url, id sender) {
[[NSNotificationCenter defaultCenter] postNotificationName:LINKS_DYNAMIC_LINK_RECEIVED
object:sender
userInfo:@{@"url": url.absoluteString}];
NSLog(@"sendDynamicLink Success: %@", url.absoluteString);
}
@implementation RNFirebaseLinks
RCT_EXPORT_MODULE();
- (id)init {
self = [super init];
if (self != nil) {
NSLog(@"Setting up RNFirebaseLinks instance");
[self initialiseLinks];
}
return self;
}
- (void)initialiseLinks {
// Set up internal listener to send notification over bridge
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(sendDynamicLinkEvent:)
name:LINKS_DYNAMIC_LINK_RECEIVED
object:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
+ (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [self handleLinkFromCustomSchemeURL:url];
}
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [self handleLinkFromCustomSchemeURL:url];
}
+(BOOL)handleLinkFromCustomSchemeURL:(NSURL *)url {
FIRDynamicLink *dynamicLink =
[[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
sendDynamicLink(dynamicLink.url, self);
return YES;
}
return NO;
}
+ (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler {
BOOL handled = [[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
if (error != nil){
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
}
else {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL* url = dynamicLink ? dynamicLink.url : userActivity.webpageURL;
sendDynamicLink(url, self);
}
}
}];
return handled;
}
- (NSArray<NSString *> *)supportedEvents {
return @[LINKS_DYNAMIC_LINK_RECEIVED];
}
- (void)sendDynamicLinkEvent:(NSNotification *)notification {
[self sendEventWithName:LINKS_DYNAMIC_LINK_RECEIVED body:notification.userInfo[@"url"]];
}
-(void)handleInitialLinkFromCustomSchemeURL:(NSURL*)url resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRDynamicLink *dynamicLink =
[[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : (id)kCFNull;
NSLog(@"initial link is: %@", urlString);
resolve(urlString);
}
-(void)handleInitialLinkFromUniversalLinkURL:(NSDictionary *)userActivityDictionary resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
NSUserActivity* userActivity = (NSUserActivity*) userActivityDictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
if ([userActivityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey] isEqual:NSUserActivityTypeBrowsingWeb])
{
[[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
if (error != nil){
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
reject(@"links/failure", @"Failed to handle universal link", error);
}
else {
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : userActivity.webpageURL.absoluteString;
NSLog(@"initial link is: %@", urlString);
resolve(urlString);
}
}];
}
else {
NSLog(@"no initial link");
resolve((id)kCFNull);
}
}
RCT_EXPORT_METHOD(getInitialLink:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
NSURL* url = (NSURL*)self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
[self handleInitialLinkFromCustomSchemeURL:url resolver:resolve rejecter:reject];
} else {
NSDictionary *userActivityDictionary =
self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
[self handleInitialLinkFromUniversalLinkURL:userActivityDictionary resolver:resolve rejecter:reject];
}
}
RCT_EXPORT_METHOD(createDynamicLink: (NSDictionary *) metadata resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
@try {
FIRDynamicLinkComponents *components = [self getDynamicLinkComponentsFromMetadata:metadata];
if (components == nil) {
reject(@"links/failure", @"Failed to create Dynamic Link", nil);
} else {
NSURL *longLink = components.url;
NSLog(@"created long dynamic link: %@", longLink.absoluteString);
resolve(longLink.absoluteString);
}
}
@catch(NSException * e) {
NSLog(@"create dynamic link failure %@", e);
reject(@"links/failure",[e reason], nil);
}
}
RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
@try {
FIRDynamicLinkComponents *components = [self getDynamicLinkComponentsFromMetadata:metadata];
[self setSuffixParameters:metadata components:components];
[components shortenWithCompletion:^(NSURL *_Nullable shortURL,
NSArray *_Nullable warnings,
NSError *_Nullable error) {
if (error) {
NSLog(@"create short dynamic link failure %@", [error localizedDescription]);
reject(@"links/failure", @"Failed to create Short Dynamic Link", error);
}
NSURL *shortLink = shortURL;
NSLog(@"created short dynamic link: %@", shortLink.absoluteString);
resolve(shortLink.absoluteString);
}];
}
@catch(NSException * e) {
NSLog(@"create short dynamic link failure %@", e);
reject(@"links/failure",[e reason], nil);
}
}
- (FIRDynamicLinkComponents *)getDynamicLinkComponentsFromMetadata:(NSDictionary *)metadata {
@try {
NSURL *link = [NSURL URLWithString:metadata[@"link"]];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:metadata[@"dynamicLinkDomain"]];
[self setAndroidParameters:metadata components:components];
[self setIosParameters:metadata components:components];
[self setSocialMetaTagParameters:metadata components:components];
return components;
}
@catch(NSException * e) {
NSLog(@"error while building componets from meta data %@", e);
@throw;
}
}
- (void)setAndroidParameters:(NSDictionary *)metadata
components:(FIRDynamicLinkComponents *)components {
NSDictionary *androidParametersDict = metadata[@"androidInfo"];
if (androidParametersDict) {
FIRDynamicLinkAndroidParameters *androidParams = [FIRDynamicLinkAndroidParameters
parametersWithPackageName: androidParametersDict[@"androidPackageName"]];
if (androidParametersDict[@"androidFallbackLink"]) {
androidParams.fallbackURL = [NSURL URLWithString:androidParametersDict[@"androidFallbackLink"]];
}
if (androidParametersDict[@"androidMinPackageVersionCode"]) {
androidParams.minimumVersion = [androidParametersDict[@"androidMinPackageVersionCode"] integerValue];
}
components.androidParameters = androidParams;
}
}
- (void)setIosParameters:(NSDictionary *)metadata
components:(FIRDynamicLinkComponents *)components {
NSDictionary *iosParametersDict = metadata[@"iosInfo"];
if (iosParametersDict) {
FIRDynamicLinkIOSParameters *iOSParams = [FIRDynamicLinkIOSParameters
parametersWithBundleID:iosParametersDict[@"iosBundleId"]];
if (iosParametersDict[@"iosAppStoreId"]) {
iOSParams.appStoreID = iosParametersDict[@"iosAppStoreId"];
}
if (iosParametersDict[@"iosCustomScheme"]) {
iOSParams.customScheme = iosParametersDict[@"iosCustomScheme"];
}
if (iosParametersDict[@"iosFallbackLink"]) {
iOSParams.fallbackURL = [NSURL URLWithString:iosParametersDict[@"iosFallbackLink"]];
}
if (iosParametersDict[@"iosIpadBundleId"]) {
iOSParams.iPadBundleID = iosParametersDict[@"iosIpadBundleId"];
}
if (iosParametersDict[@"iosIpadFallbackLink"]) {
iOSParams.iPadFallbackURL = [NSURL URLWithString:iosParametersDict[@"iosIpadFallbackLink"]];
}
if (iosParametersDict[@"iosMinPackageVersionCode"]) {
iOSParams.minimumAppVersion = iosParametersDict[@"iosMinPackageVersionCode"];
}
components.iOSParameters = iOSParams;
}
}
- (void)setSocialMetaTagParameters:(NSDictionary *)metadata
components:(FIRDynamicLinkComponents *)components {
NSDictionary *socialParamsDict = metadata[@"socialMetaTagInfo"];
if (socialParamsDict) {
FIRDynamicLinkSocialMetaTagParameters *socialParams = [FIRDynamicLinkSocialMetaTagParameters parameters];
if (socialParamsDict[@"socialTitle"]) {
socialParams.title = socialParamsDict[@"socialTitle"];
}
if (socialParamsDict[@"socialDescription"]) {
socialParams.descriptionText = socialParamsDict[@"socialDescription"];
}
if (socialParamsDict[@"socialImageLink"]) {
socialParams.imageURL = [NSURL URLWithString:socialParamsDict[@"socialImageLink"]];
}
components.socialMetaTagParameters = socialParams;
}
}
- (void)setSuffixParameters:(NSDictionary *)metadata
components:(FIRDynamicLinkComponents *)components {
NSDictionary *suffixParametersDict = metadata[@"suffix"];
if (suffixParametersDict) {
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
if ([suffixParametersDict[@"option"] isEqual: @"SHORT"]) {
options.pathLength = FIRShortDynamicLinkPathLengthShort;
}
else if ([suffixParametersDict[@"option"] isEqual: @"UNGUESSABLE"]) {
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
}
components.options = options;
}
}
@end
#else
@implementation RNFirebaseLinks
@end
#endif

View File

@ -13,6 +13,7 @@ import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
import Messaging, { statics as MessagingStatics } from './modules/messaging';
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
import Links, { statics as LinksStatics } from './modules/links';
import Utils, { statics as UtilsStatics } from './modules/utils';
const FirebaseCoreModule = NativeModules.RNFirebase;
@ -35,6 +36,7 @@ export default class FirebaseApp {
this.crash = this._staticsOrModuleInstance({}, Crash);
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
this.perf = this._staticsOrModuleInstance({}, Performance);
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);

View File

@ -14,6 +14,7 @@ import Auth, { statics as AuthStatics } from './modules/auth';
import Analytics from './modules/analytics';
import Crash from './modules/crash';
import Performance from './modules/perf';
import Links, { statics as LinksStatics } from './modules/links';
import RemoteConfig from './modules/config';
import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
@ -42,6 +43,7 @@ class FirebaseCore {
this.crash = this._appNamespaceOrStatics({}, Crash);
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);

121
lib/modules/links/index.js Normal file
View File

@ -0,0 +1,121 @@
import ModuleBase from './../../utils/ModuleBase';
import { areObjectKeysContainedInOther, isObject, isString } from './../../utils';
const EVENT_TYPE = {
Link: 'dynamic_link_received',
};
function validateParameters(parameters: Object): void {
const suportedParametersObject = {
dynamicLinkDomain: 'string',
link: 'string',
androidInfo: {
androidPackageName: 'string',
androidFallbackLink: 'string',
androidMinPackageVersionCode: 'string',
androidLink: 'string',
},
iosInfo: {
iosBundleId: 'string',
iosFallbackLink: 'string',
iosCustomScheme: 'string',
iosIpadFallbackLink: 'string',
iosIpadBundleId: 'string',
iosAppStoreId: 'string',
},
socialMetaTagInfo: {
socialTitle: 'string',
socialDescription: 'string',
socialImageLink: 'string',
},
suffix: {
option: 'string',
},
};
if (!areObjectKeysContainedInOther(parameters, suportedParametersObject)) {
throw new Error('Invalid Parameters.');
}
}
function checkForMandatoryParameters(parameters: Object): void {
if (!isString(parameters.dynamicLinkDomain)) {
throw new Error('No dynamicLinkDomain was specified.');
}
if (!isString(parameters.link)) {
throw new Error('No link was specified.');
}
if (isObject(parameters.androidInfo) && !isString(parameters.androidInfo.androidPackageName)) {
throw new Error('No androidPackageName was specified.');
}
if (isObject(parameters.iosInfo) && !isString(parameters.iosInfo.iosBundleId)) {
throw new Error('No iosBundleId was specified.');
}
}
/**
* @class Links
*/
export default class Links extends ModuleBase {
static _NAMESPACE = 'links';
static _NATIVE_MODULE = 'RNFirebaseLinks';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
}
get EVENT_TYPE() {
return EVENT_TYPE;
}
/**
* Returns the link that triggered application open
* @returns {Promise.<String>}
*/
getInitialLink() {
return this._native.getInitialLink();
}
/**
* Subscribe to dynamic links
* @param listener
* @returns {Function}
*/
onLink(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.Link, listener);
return () => rnListener.remove();
}
/**
* Create long Dynamic Link from parameters
* @param parameters
* @returns {Promise.<String>}
*/
createDynamicLink(parameters: Object = {}): Promise<String> {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}
}
/**
* Create short Dynamic Link from parameters
* @param parameters
* @returns {Promise.<String>}
*/
createShortDynamicLink(parameters: Object = {}): Promise<String> {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createShortDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}
}
}
export const statics = {
EVENT_TYPE,
};

View File

@ -70,6 +70,43 @@ export function deepExists(object: Object,
return tmp !== undefined;
}
/**
* Deep Check if obj1 keys are contained in obj2
* @param obj1
* @param obj2
* @returns {boolean}
*/
export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): boolean {
if (!isObject(obj1) || !isObject(obj2)) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (isArrayContainedInOther(keys1, keys2)) {
return keys1.filter((key) => {
return isObject(obj1[key]);
}).reduce((acc, cur) => {
return acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]);
}, true);
}
return false;
}
/**
* Check if arr1 is contained in arr2
* @param arr1
* @param arr2
* @returns {boolean}
*/
export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false;
}
return arr1.reduce((acc, cur) => {
return acc && arr2.includes(cur);
}, true);
}
/**
* Simple is object check.
* @param item

View File

@ -36,6 +36,7 @@
},
"globals": {
"__DEV__": true,
"window": true
"window": true,
"fetch": true,
}
}

View File

@ -92,6 +92,7 @@ dependencies {
compile "com.google.firebase:firebase-perf:$firebaseVersion"
compile "com.google.firebase:firebase-storage:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$firebaseVersion"
compile "com.android.support:appcompat-v7:26.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
}

View File

@ -11,6 +11,7 @@ import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage;
import io.invertase.firebase.crash.RNFirebaseCrashPackage;
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
import io.invertase.firebase.links.RNFirebaseLinksPackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
@ -44,6 +45,7 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseCrashPackage(),
new RNFirebaseDatabasePackage(),
new RNFirebaseFirestorePackage(),
new RNFirebaseLinksPackage(),
new RNFirebaseMessagingPackage(),
new RNFirebasePerformancePackage(),
new RNFirebaseStoragePackage()

840
tests/package-lock.json generated
View File

@ -2047,7 +2047,6 @@
"requires": {
"anymatch": "1.3.2",
"async-each": "1.0.1",
"fsevents": "1.1.2",
"glob-parent": "2.0.0",
"inherits": "2.0.3",
"is-binary-path": "1.0.1",
@ -2525,6 +2524,11 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-diff": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.4.tgz",
@ -3804,791 +3808,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
"integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
"optional": true,
"requires": {
"nan": "2.7.0",
"node-pre-gyp": "0.6.36"
},
"dependencies": {
"abbrev": {
"version": "1.1.0",
"bundled": true,
"optional": true
},
"ajv": {
"version": "4.11.8",
"bundled": true,
"optional": true,
"requires": {
"co": "4.6.0",
"json-stable-stringify": "1.0.1"
}
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
},
"aproba": {
"version": "1.1.1",
"bundled": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"bundled": true,
"optional": true,
"requires": {
"delegates": "1.0.0",
"readable-stream": "2.2.9"
}
},
"asn1": {
"version": "0.2.3",
"bundled": true,
"optional": true
},
"assert-plus": {
"version": "0.2.0",
"bundled": true,
"optional": true
},
"asynckit": {
"version": "0.4.0",
"bundled": true,
"optional": true
},
"aws-sign2": {
"version": "0.6.0",
"bundled": true,
"optional": true
},
"aws4": {
"version": "1.6.0",
"bundled": true,
"optional": true
},
"balanced-match": {
"version": "0.4.2",
"bundled": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"bundled": true,
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"block-stream": {
"version": "0.0.9",
"bundled": true,
"requires": {
"inherits": "2.0.3"
}
},
"boom": {
"version": "2.10.1",
"bundled": true,
"requires": {
"hoek": "2.16.3"
}
},
"brace-expansion": {
"version": "1.1.7",
"bundled": true,
"requires": {
"balanced-match": "0.4.2",
"concat-map": "0.0.1"
}
},
"buffer-shims": {
"version": "1.0.0",
"bundled": true
},
"caseless": {
"version": "0.12.0",
"bundled": true,
"optional": true
},
"co": {
"version": "4.6.0",
"bundled": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
},
"combined-stream": {
"version": "1.0.5",
"bundled": true,
"requires": {
"delayed-stream": "1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true
},
"cryptiles": {
"version": "2.0.5",
"bundled": true,
"optional": true,
"requires": {
"boom": "2.10.1"
}
},
"dashdash": {
"version": "1.14.1",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"debug": {
"version": "2.6.8",
"bundled": true,
"optional": true,
"requires": {
"ms": "2.0.0"
}
},
"deep-extend": {
"version": "0.4.2",
"bundled": true,
"optional": true
},
"delayed-stream": {
"version": "1.0.0",
"bundled": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"optional": true
},
"ecc-jsbn": {
"version": "0.1.1",
"bundled": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"extend": {
"version": "3.0.1",
"bundled": true,
"optional": true
},
"extsprintf": {
"version": "1.0.2",
"bundled": true
},
"forever-agent": {
"version": "0.6.1",
"bundled": true,
"optional": true
},
"form-data": {
"version": "2.1.4",
"bundled": true,
"optional": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.15"
}
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true
},
"fstream": {
"version": "1.0.11",
"bundled": true,
"requires": {
"graceful-fs": "4.1.11",
"inherits": "2.0.3",
"mkdirp": "0.5.1",
"rimraf": "2.6.1"
}
},
"fstream-ignore": {
"version": "1.0.5",
"bundled": true,
"optional": true,
"requires": {
"fstream": "1.0.11",
"inherits": "2.0.3",
"minimatch": "3.0.4"
}
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"optional": true,
"requires": {
"aproba": "1.1.1",
"console-control-strings": "1.1.0",
"has-unicode": "2.0.1",
"object-assign": "4.1.1",
"signal-exit": "3.0.2",
"string-width": "1.0.2",
"strip-ansi": "3.0.1",
"wide-align": "1.1.2"
}
},
"getpass": {
"version": "0.1.7",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"glob": {
"version": "7.1.2",
"bundled": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"bundled": true
},
"har-schema": {
"version": "1.0.5",
"bundled": true,
"optional": true
},
"har-validator": {
"version": "4.2.1",
"bundled": true,
"optional": true,
"requires": {
"ajv": "4.11.8",
"har-schema": "1.0.5"
}
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"optional": true
},
"hawk": {
"version": "3.1.3",
"bundled": true,
"optional": true,
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
"hoek": "2.16.3",
"sntp": "1.0.9"
}
},
"hoek": {
"version": "2.16.3",
"bundled": true
},
"http-signature": {
"version": "1.1.1",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.0",
"sshpk": "1.13.0"
}
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"bundled": true
},
"ini": {
"version": "1.3.4",
"bundled": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"requires": {
"number-is-nan": "1.0.1"
}
},
"is-typedarray": {
"version": "1.0.0",
"bundled": true,
"optional": true
},
"isarray": {
"version": "1.0.0",
"bundled": true
},
"isstream": {
"version": "0.1.2",
"bundled": true,
"optional": true
},
"jodid25519": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"jsbn": {
"version": "0.1.1",
"bundled": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",
"bundled": true,
"optional": true
},
"json-stable-stringify": {
"version": "1.0.1",
"bundled": true,
"optional": true,
"requires": {
"jsonify": "0.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true,
"optional": true
},
"jsonify": {
"version": "0.0.0",
"bundled": true,
"optional": true
},
"jsprim": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.0.2",
"json-schema": "0.2.3",
"verror": "1.3.6"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"mime-db": {
"version": "1.27.0",
"bundled": true
},
"mime-types": {
"version": "2.1.15",
"bundled": true,
"requires": {
"mime-db": "1.27.0"
}
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"requires": {
"brace-expansion": "1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"bundled": true,
"optional": true
},
"node-pre-gyp": {
"version": "0.6.36",
"bundled": true,
"optional": true,
"requires": {
"mkdirp": "0.5.1",
"nopt": "4.0.1",
"npmlog": "4.1.0",
"rc": "1.2.1",
"request": "2.81.0",
"rimraf": "2.6.1",
"semver": "5.3.0",
"tar": "2.2.1",
"tar-pack": "3.4.0"
}
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"optional": true,
"requires": {
"abbrev": "1.1.0",
"osenv": "0.1.4"
}
},
"npmlog": {
"version": "4.1.0",
"bundled": true,
"optional": true,
"requires": {
"are-we-there-yet": "1.1.4",
"console-control-strings": "1.1.0",
"gauge": "2.7.4",
"set-blocking": "2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
},
"oauth-sign": {
"version": "0.8.2",
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"requires": {
"wrappy": "1.0.2"
}
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"optional": true
},
"osenv": {
"version": "0.1.4",
"bundled": true,
"optional": true,
"requires": {
"os-homedir": "1.0.2",
"os-tmpdir": "1.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true
},
"performance-now": {
"version": "0.2.0",
"bundled": true,
"optional": true
},
"process-nextick-args": {
"version": "1.0.7",
"bundled": true
},
"punycode": {
"version": "1.4.1",
"bundled": true,
"optional": true
},
"qs": {
"version": "6.4.0",
"bundled": true,
"optional": true
},
"rc": {
"version": "1.2.1",
"bundled": true,
"optional": true,
"requires": {
"deep-extend": "0.4.2",
"ini": "1.3.4",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.2.9",
"bundled": true,
"requires": {
"buffer-shims": "1.0.0",
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "1.0.1",
"util-deprecate": "1.0.2"
}
},
"request": {
"version": "2.81.0",
"bundled": true,
"optional": true,
"requires": {
"aws-sign2": "0.6.0",
"aws4": "1.6.0",
"caseless": "0.12.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "4.2.1",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.15",
"oauth-sign": "0.8.2",
"performance-now": "0.2.0",
"qs": "6.4.0",
"safe-buffer": "5.0.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.2",
"tunnel-agent": "0.6.0",
"uuid": "3.0.1"
}
},
"rimraf": {
"version": "2.6.1",
"bundled": true,
"requires": {
"glob": "7.1.2"
}
},
"safe-buffer": {
"version": "5.0.1",
"bundled": true
},
"semver": {
"version": "5.3.0",
"bundled": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"optional": true
},
"sntp": {
"version": "1.0.9",
"bundled": true,
"optional": true,
"requires": {
"hoek": "2.16.3"
}
},
"sshpk": {
"version": "1.13.0",
"bundled": true,
"optional": true,
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jodid25519": "1.0.2",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"string_decoder": {
"version": "1.0.1",
"bundled": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"requires": {
"code-point-at": "1.1.0",
"is-fullwidth-code-point": "1.0.0",
"strip-ansi": "3.0.1"
}
},
"stringstream": {
"version": "0.0.5",
"bundled": true,
"optional": true
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"requires": {
"ansi-regex": "2.1.1"
}
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"optional": true
},
"tar": {
"version": "2.2.1",
"bundled": true,
"requires": {
"block-stream": "0.0.9",
"fstream": "1.0.11",
"inherits": "2.0.3"
}
},
"tar-pack": {
"version": "3.4.0",
"bundled": true,
"optional": true,
"requires": {
"debug": "2.6.8",
"fstream": "1.0.11",
"fstream-ignore": "1.0.5",
"once": "1.4.0",
"readable-stream": "2.2.9",
"rimraf": "2.6.1",
"tar": "2.2.1",
"uid-number": "0.0.6"
}
},
"tough-cookie": {
"version": "2.3.2",
"bundled": true,
"optional": true,
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"bundled": true,
"optional": true
},
"uid-number": {
"version": "0.0.6",
"bundled": true,
"optional": true
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true
},
"uuid": {
"version": "3.0.1",
"bundled": true,
"optional": true
},
"verror": {
"version": "1.3.6",
"bundled": true,
"optional": true,
"requires": {
"extsprintf": "1.0.2"
}
},
"wide-align": {
"version": "1.1.2",
"bundled": true,
"optional": true,
"requires": {
"string-width": "1.0.2"
}
},
"wrappy": {
"version": "1.0.2",
"bundled": true
}
}
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -6713,12 +5932,6 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"nan": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
"optional": true
},
"native-promise-only": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
@ -7235,6 +6448,21 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz",
"integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc="
},
"query-string": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.0.0.tgz",
"integrity": "sha1-+99wBLTSr/eS+YcZgbeieU9VWUc=",
"requires": {
"decode-uri-component": "0.2.0",
"object-assign": "4.1.1",
"strict-uri-encode": "1.1.0"
}
},
"querystringify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz",
"integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
@ -7987,6 +7215,11 @@
"resolve-from": "1.0.1"
}
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"resolve": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
@ -8087,7 +7320,6 @@
"anymatch": "1.3.2",
"exec-sh": "0.2.1",
"fb-watchman": "2.0.0",
"fsevents": "1.1.2",
"minimatch": "3.0.4",
"minimist": "1.2.0",
"walker": "1.0.7",
@ -8497,10 +7729,10 @@
"readable-stream": "1.1.14"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string-length": {
"version": "1.0.1",
@ -8535,6 +7767,11 @@
}
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@ -8859,6 +8096,15 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"url-parse": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz",
"integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=",
"requires": {
"querystringify": "1.0.0",
"requires-port": "1.0.0"
}
},
"user-home": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",

View File

@ -33,6 +33,7 @@
"lodash.groupby": "^4.6.0",
"lodash.some": "^4.6.0",
"prop-types": "^15.6.0",
"query-string": "^5.0.0",
"react": "^16.0.0",
"react-native": "^0.49.1",
"react-native-vector-icons": "^4.0.0",
@ -45,7 +46,8 @@
"redux-thunk": "^2.2.0",
"should": "^11.2.0",
"should-sinon": "^0.0.5",
"sinon": "^3.2.1"
"sinon": "^3.2.1",
"url-parse": "^1.1.9"
},
"devDependencies": {
"babel-cli": "^6.24.0",

View File

@ -10,6 +10,7 @@ import config from './config';
import performance from './perf';
import admob from './admob';
import firestore from './firestore';
import links from './links/index';
window.getCoverage = function getCoverage() {
return (JSON.stringify(global.__coverage__));
@ -27,6 +28,7 @@ const testSuiteInstances = [
messaging,
performance,
storage,
links,
];
/*

View File

@ -0,0 +1,9 @@
import firebase from '../../firebase';
import TestSuite from '../../../lib/TestSuite';
import linksTests from './linksTests';
const suite = new TestSuite('Links', 'firebase.links()', firebase);
suite.addTests(linksTests);
export default suite;

View File

@ -0,0 +1,439 @@
import should from 'should';
import URL from 'url-parse';
import queryString from 'query-string';
function linksTests({ describe, it, firebase, tryCatch }) {
describe('test links', () => {
const links = firebase.native.links();
const link = 'https://yoursite.example.com';
const dynamicLinkDomain = 'x59dg.app.goo.gl';
const androidPackageName = 'com.reactnativefirebasedemo';
const androidFallbackLink = 'android.fallback.com';
const androidMinPackageVersionCode = '42';
const iosBundleId = 'com.invertase.ReactNativeFirebaseDemo';
const iosFallbackLink = 'ios.fallback.com';
const iosCustomScheme = 'custom';
const iosIpadFallbackLink = 'ios.ipad.fallback.com';
const iosIpadBundleId = 'com.invertase.ReactNativeFirebaseDemo';
const iosAppStoreId = '0123456789';
const socialTitle = 'title';
const socialDescription = 'description';
const socialImageLink = 'test.imageUrl.com';
it('create long dynamic link with all supported parameters', async () => {
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
androidFallbackLink,
androidMinPackageVersionCode,
},
iosInfo: {
iosBundleId,
iosFallbackLink,
iosCustomScheme,
iosIpadFallbackLink,
iosIpadBundleId,
iosAppStoreId,
},
socialMetaTagInfo: {
socialTitle,
socialDescription,
socialImageLink,
},
};
const result = await links.createDynamicLink(data);
const expectedParameters = { sd: socialDescription,
si: socialImageLink,
st: socialTitle,
afl: androidFallbackLink,
amv: androidMinPackageVersionCode,
apn: androidPackageName,
ibi: iosBundleId,
ifl: iosFallbackLink,
isi: iosAppStoreId,
ius: iosCustomScheme,
ipbi: iosIpadBundleId,
ipfl: iosIpadFallbackLink,
link,
};
const url = new URL(result);
url.protocol.should.eql('https:');
url.hostname.should.eql(dynamicLinkDomain);
const params = queryString.parse(url.query);
Object.keys(expectedParameters).forEach((key) => {
const val = expectedParameters[key];
params[key].should.eql(val);
});
});
it('create long dynamic link with minimal parameters', async () => {
const data = {
link,
dynamicLinkDomain,
};
const result = await links.createDynamicLink(data);
const url = new URL(result);
url.protocol.should.eql('https:');
url.hostname.should.eql(dynamicLinkDomain);
const params = queryString.parse(url.query);
params.link.should.eql(link);
});
it('fail to create long dynamic link with empty data object', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('No dynamicLinkDomain was specified.');
resolve();
}, reject);
const data = { };
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link without link object', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('No link was specified.');
resolve();
}, reject);
const data = { dynamicLinkDomain };
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link without iosBundleId', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('No iosBundleId was specified.');
resolve();
}, reject);
// Setup
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
androidFallbackLink,
androidMinPackageVersionCode,
},
iosInfo: {
iosFallbackLink,
iosCustomScheme,
iosIpadFallbackLink,
iosIpadBundleId,
iosAppStoreId,
},
socialMetaTagInfo: {
socialTitle,
socialDescription,
socialImageLink,
},
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link without androidPackageName', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('No androidPackageName was specified.');
resolve();
}, reject);
// Setup
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidFallbackLink,
androidMinPackageVersionCode,
},
iosInfo: {
iosBundleId,
iosFallbackLink,
iosCustomScheme,
iosIpadFallbackLink,
iosIpadBundleId,
iosAppStoreId,
},
socialMetaTagInfo: {
socialTitle,
socialDescription,
socialImageLink,
},
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link with unsupported parameter', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('Invalid Parameters.');
resolve();
}, reject);
const data = {
link,
dynamicLinkDomain,
someInvalidParameter: 'invalid',
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link with unsupported ios parameters', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('Invalid Parameters.');
resolve();
}, reject);
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
},
iosInfo: {
iosBundleId,
someInvalidParameter: 'invalid',
someOtherParameter: 'invalid',
},
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link with unsupported android parameters', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('Invalid Parameters.');
resolve();
}, reject);
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
someInvalidParameter: 'invalid',
someOtherParameter: 'invalid',
},
iosInfo: {
iosBundleId,
},
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('fail to create long dynamic link with unsupported social parameters', () => {
return new Promise((resolve, reject) => {
const success = tryCatch(() => {
// Assertion
reject(new Error('createDynamicLink did not fail.'));
}, reject);
const failure = tryCatch((error) => {
// Assertion
error.message.should.equal('Invalid Parameters.');
resolve();
}, reject);
const data = {
link,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
},
iosInfo: {
iosBundleId,
},
socialMetaTagInfo: {
someInvalidParameter: 'invalid',
someOtherParameter: 'invalid',
},
};
// Test
links.createDynamicLink(data)
.then(success)
.catch(failure);
});
});
it('create short (unguessable) dynamic link with all supported parameters', async () => {
const url = 'https://www.google.co.il/search?q=react+native+firebase';
const data = {
link: url,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
androidFallbackLink,
androidMinPackageVersionCode,
},
iosInfo: {
iosBundleId,
iosFallbackLink,
iosCustomScheme,
iosIpadFallbackLink,
iosIpadBundleId,
iosAppStoreId,
},
socialMetaTagInfo: {
socialTitle,
socialDescription,
socialImageLink,
},
};
const result = await links.createShortDynamicLink(data);
result.should.startWith(`https://${dynamicLinkDomain}`);
const response = await fetch(result);
url.should.eql(response.url);
});
it('create short (short) dynamic link with all supported parameters', async () => {
const url = 'https://www.google.co.il/search?q=react+native+firebase';
const data = {
link: url,
dynamicLinkDomain,
androidInfo: {
androidPackageName,
androidFallbackLink,
androidMinPackageVersionCode,
},
iosInfo: {
iosBundleId,
iosFallbackLink,
iosCustomScheme,
iosIpadFallbackLink,
iosIpadBundleId,
iosAppStoreId,
},
socialMetaTagInfo: {
socialTitle,
socialDescription,
socialImageLink,
},
suffix: {
option: 'SHORT',
},
};
const result = await links.createShortDynamicLink(data);
result.should.startWith(`https://${dynamicLinkDomain}`);
const response = await fetch(result);
url.should.eql(response.url);
});
it('getInitialLink should return null', async () => {
const initialLink = await links.getInitialLink();
should(initialLink).be.null();
});
it('should listen to link', () => {
const unsubscribe = links.onLink((url: string) => {
console.log(url);
// handle link
});
unsubscribe();
});
});
}
export default linksTests;