[invites] Initial JS and Android invites functionality
This commit is contained in:
parent
26c3ed9a60
commit
53babb4cd9
@ -0,0 +1,236 @@
|
||||
package io.invertase.firebase.invites;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
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.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.android.gms.appinvite.AppInviteInvitation;
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.firebase.appinvite.FirebaseAppInvite;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
import io.invertase.firebase.links.RNFirebaseLinks;
|
||||
|
||||
public class RNFirebaseInvites extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
|
||||
private static final String TAG = "RNFirebaseInvites";
|
||||
private static final int REQUEST_INVITE = 81283;
|
||||
private boolean mInitialInvitationInitialized = false;
|
||||
private String mInitialDeepLink = null;
|
||||
private String mInitialInvitationId = null;
|
||||
private Promise mPromise = null;
|
||||
|
||||
public RNFirebaseInvites(ReactApplicationContext context) {
|
||||
super(context);
|
||||
getReactApplicationContext().addActivityEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNFirebaseInvites";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getInitialInvitation(final Promise promise) {
|
||||
if (mInitialInvitationInitialized) {
|
||||
if (mInitialDeepLink != null || mInitialInvitationId != null) {
|
||||
promise.resolve(buildInvitationMap(mInitialDeepLink, mInitialInvitationId));
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
} else {
|
||||
if (getCurrentActivity() != null) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(getCurrentActivity().getIntent())
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null) {
|
||||
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
|
||||
if (invite == null) {
|
||||
promise.resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
mInitialDeepLink = pendingDynamicLinkData.getLink().toString();
|
||||
mInitialInvitationId = invite.getInvitationId();
|
||||
promise.resolve(buildInvitationMap(mInitialDeepLink, mInitialInvitationId));
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
mInitialInvitationInitialized = true;
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
Log.e(TAG, "getInitialInvitation: failed to resolve invitation", e);
|
||||
promise.reject("invites/initial-invitation-error", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "getInitialInvitation: activity is null");
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendInvitation(ReadableMap invitationMap, Promise promise) {
|
||||
if (!invitationMap.hasKey("message")) {
|
||||
promise.reject("invites/invalid-invitation", "The supplied invitation is missing a 'message' field");
|
||||
return;
|
||||
}
|
||||
if (!invitationMap.hasKey("title")) {
|
||||
promise.reject("invites/invalid-invitation", "The supplied invitation is missing a 'title' field");
|
||||
return;
|
||||
}
|
||||
|
||||
AppInviteInvitation.IntentBuilder ib = new AppInviteInvitation.IntentBuilder(invitationMap.getString("title"));
|
||||
if (invitationMap.hasKey("androidMinimumVersionCode")) {
|
||||
Double androidMinimumVersionCode = invitationMap.getDouble("androidMinimumVersionCode");
|
||||
ib = ib.setAndroidMinimumVersionCode(androidMinimumVersionCode.intValue());
|
||||
}
|
||||
if (invitationMap.hasKey("callToActionText")) {
|
||||
ib = ib.setCallToActionText(invitationMap.getString("callToActionText"));
|
||||
}
|
||||
if (invitationMap.hasKey("customImage")) {
|
||||
ib = ib.setCustomImage(Uri.parse(invitationMap.getString("customImage")));
|
||||
}
|
||||
if (invitationMap.hasKey("deepLink")) {
|
||||
ib = ib.setDeepLink(Uri.parse(invitationMap.getString("deepLink")));
|
||||
}
|
||||
if (invitationMap.hasKey("iosClientId")) {
|
||||
ib = ib.setOtherPlatformsTargetApplication(
|
||||
AppInviteInvitation.IntentBuilder.PlatformMode.PROJECT_PLATFORM_IOS,
|
||||
invitationMap.getString("iosClientId"));
|
||||
}
|
||||
ib = ib.setMessage(invitationMap.getString("message"));
|
||||
|
||||
// Android specific properties
|
||||
if (invitationMap.hasKey("android")) {
|
||||
ReadableMap androidMap = invitationMap.getMap("android");
|
||||
|
||||
if (androidMap.hasKey("additionalReferralParameters")) {
|
||||
Map<String, String> arpMap = new HashMap<>();
|
||||
ReadableMap arpReadableMap = androidMap.getMap("additionalReferralParameters");
|
||||
ReadableMapKeySetIterator iterator = arpReadableMap.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
arpMap.put(key, arpReadableMap.getString(key));
|
||||
}
|
||||
ib = ib.setAdditionalReferralParameters(arpMap);
|
||||
}
|
||||
if (androidMap.hasKey("emailHtmlContent")) {
|
||||
ib = ib.setEmailHtmlContent(invitationMap.getString("emailHtmlContent"));
|
||||
}
|
||||
if (androidMap.hasKey("emailSubject")) {
|
||||
ib = ib.setEmailSubject(invitationMap.getString("emailSubject"));
|
||||
}
|
||||
if (androidMap.hasKey("googleAnalyticsTrackingId")) {
|
||||
ib = ib.setGoogleAnalyticsTrackingId(invitationMap.getString("googleAnalyticsTrackingId"));
|
||||
}
|
||||
}
|
||||
|
||||
Intent invitationIntent = ib.build();
|
||||
// Save the promise for later
|
||||
this.mPromise = promise;
|
||||
|
||||
// Start the intent
|
||||
this.getCurrentActivity().startActivityForResult(invitationIntent, REQUEST_INVITE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_INVITE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
|
||||
mPromise.resolve(Arguments.fromList(Arrays.asList(ids)));
|
||||
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
mPromise.reject("invites/invitation-cancelled", " The invitation was cancelled");
|
||||
} else {
|
||||
mPromise.reject("invites/invitation-error", " The invitation failed to send");
|
||||
}
|
||||
// Clear the promise
|
||||
mPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(intent)
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null) {
|
||||
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
|
||||
if (invite == null) {
|
||||
// this is a dynamic link, not an invitation
|
||||
return;
|
||||
}
|
||||
|
||||
String deepLink = pendingDynamicLinkData.getLink().toString();
|
||||
String invitationId = invite.getInvitationId();
|
||||
WritableMap invitationMap = buildInvitationMap(deepLink, invitationId);
|
||||
Utils.sendEvent(getReactApplicationContext(), "invites_invitation_received", invitationMap);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
// Not required for this module
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
// Not required for this module
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
mInitialDeepLink = null;
|
||||
mInitialInvitationId = null;
|
||||
mInitialInvitationInitialized = false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private WritableMap buildInvitationMap(String deepLink, String invitationId) {
|
||||
WritableMap invitationMap = Arguments.createMap();
|
||||
invitationMap.putString("deepLink", deepLink);
|
||||
invitationMap.putString("invitationId", invitationId);
|
||||
|
||||
return invitationMap;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.invertase.firebase.invites;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
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 RNFirebaseInvitesPackage implements ReactPackage {
|
||||
public RNFirebaseInvitesPackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 RNFirebaseInvites(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();
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.firebase.appinvite.FirebaseAppInvite;
|
||||
import com.google.firebase.dynamiclinks.DynamicLink;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import com.google.firebase.dynamiclinks.ShortDynamicLink;
|
||||
@ -33,14 +35,6 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
||||
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);
|
||||
@ -52,101 +46,6 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
||||
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 {
|
||||
@ -190,6 +89,95 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getInitialLink(final Promise promise) {
|
||||
if (mInitialLinkInitialized) {
|
||||
promise.resolve(mInitialLink);
|
||||
} else {
|
||||
if (getCurrentActivity() != null) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(getCurrentActivity().getIntent())
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null
|
||||
&& !isInvitation(pendingDynamicLinkData)) {
|
||||
|
||||
mInitialLink = pendingDynamicLinkData.getLink().toString();
|
||||
}
|
||||
Log.d(TAG, "getInitialLink: link is: " + mInitialLink);
|
||||
mInitialLinkInitialized = true;
|
||||
promise.resolve(mInitialLink);
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
Log.e(TAG, "getInitialLink: failed to resolve link", e);
|
||||
promise.reject("link/initial-link-error", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "getInitialLink: activity is null");
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
// Not required for this module
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(intent)
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null
|
||||
&& !isInvitation(pendingDynamicLinkData)) {
|
||||
String link = pendingDynamicLinkData.getLink().toString();
|
||||
Utils.sendEvent(getReactApplicationContext(), "links_link_received", link);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@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;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Looks at the internals of the link data to detect whether it's an invitation or not
|
||||
private boolean isInvitation(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
return pendingDynamicLinkData.zzcbj().getString("com.google.firebase.appinvite.fdl.extension.InvitationId") != null;
|
||||
}
|
||||
|
||||
private DynamicLink.Builder getDynamicLinkBuilderFromMap(final Map<String, Object> metaData) {
|
||||
DynamicLink.Builder parametersBuilder = FirebaseDynamicLinks.getInstance().createDynamicLink();
|
||||
try {
|
||||
|
@ -49,6 +49,6 @@ 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";
|
||||
static NSString *const LINKS_DYNAMIC_LINK_RECEIVED = @"links_link_received";
|
||||
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@ import Crashlytics, {
|
||||
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
|
||||
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
|
||||
import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../instanceid';
|
||||
import Invites, { NAMESPACE as InvitesNamespace } from '../invites';
|
||||
import Links, { NAMESPACE as LinksNamespace } from '../links';
|
||||
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
|
||||
import Notifications, {
|
||||
@ -49,6 +50,7 @@ export default class App {
|
||||
};
|
||||
firestore: () => Firestore;
|
||||
instanceid: () => InstanceId;
|
||||
invites: () => Invites;
|
||||
links: () => Links;
|
||||
messaging: () => Messaging;
|
||||
notifications: () => Notifications;
|
||||
@ -90,6 +92,7 @@ export default class App {
|
||||
};
|
||||
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
|
||||
this.instanceid = APPS.appModule(this, InstanceIdNamespace, InstanceId);
|
||||
this.invites = APPS.appModule(this, InvitesNamespace, Invites);
|
||||
this.links = APPS.appModule(this, LinksNamespace, Links);
|
||||
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
|
||||
this.notifications = APPS.appModule(
|
||||
|
@ -42,6 +42,10 @@ import {
|
||||
statics as InstanceIdStatics,
|
||||
MODULE_NAME as InstanceIdModuleName,
|
||||
} from '../instanceid';
|
||||
import {
|
||||
statics as InvitesStatics,
|
||||
MODULE_NAME as InvitesModuleName,
|
||||
} from '../invites';
|
||||
import {
|
||||
statics as LinksStatics,
|
||||
MODULE_NAME as LinksModuleName,
|
||||
@ -78,6 +82,7 @@ import type {
|
||||
FirebaseOptions,
|
||||
FirestoreModule,
|
||||
InstanceIdModule,
|
||||
InvitesModule,
|
||||
LinksModule,
|
||||
MessagingModule,
|
||||
NotificationsModule,
|
||||
@ -98,6 +103,7 @@ class Firebase {
|
||||
fabric: FabricModule;
|
||||
firestore: FirestoreModule;
|
||||
instanceid: InstanceIdModule;
|
||||
invites: InvitesModule;
|
||||
links: LinksModule;
|
||||
messaging: MessagingModule;
|
||||
notifications: NotificationsModule;
|
||||
@ -147,6 +153,11 @@ class Firebase {
|
||||
InstanceIdStatics,
|
||||
InstanceIdModuleName
|
||||
);
|
||||
this.invites = APPS.moduleAndStatics(
|
||||
'invites',
|
||||
InvitesStatics,
|
||||
InvitesModuleName
|
||||
);
|
||||
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
|
||||
this.messaging = APPS.moduleAndStatics(
|
||||
'messaging',
|
||||
|
69
lib/modules/invites/AndroidInvitation.js
Normal file
69
lib/modules/invites/AndroidInvitation.js
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @flow
|
||||
* AndroidInvitation representation wrapper
|
||||
*/
|
||||
import type Invitation from './Invitation';
|
||||
import type { NativeAndroidInvitation } from './types';
|
||||
|
||||
export default class AndroidInvitation {
|
||||
_additionalReferralParameters: { [string]: string } | void;
|
||||
_emailHtmlContent: string | void;
|
||||
_emailSubject: string | void;
|
||||
_googleAnalyticsTrackingId: string | void;
|
||||
_invitation: Invitation;
|
||||
|
||||
constructor(invitation: Invitation) {
|
||||
this._invitation = invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param additionalReferralParameters
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAdditionalReferralParameters(additionalReferralParameters: {
|
||||
[string]: string,
|
||||
}): Invitation {
|
||||
this._additionalReferralParameters = additionalReferralParameters;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailHtmlContent
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailHtmlContent(emailHtmlContent: string): Invitation {
|
||||
this._emailHtmlContent = emailHtmlContent;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailSubject
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailSubject(emailSubject: string): Invitation {
|
||||
this._emailSubject = emailSubject;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param googleAnalyticsTrackingId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setGoogleAnalyticsTrackingId(googleAnalyticsTrackingId: string): Invitation {
|
||||
this._googleAnalyticsTrackingId = googleAnalyticsTrackingId;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
build(): NativeAndroidInvitation {
|
||||
return {
|
||||
additionalReferralParameters: this._additionalReferralParameters,
|
||||
emailHtmlContent: this._emailHtmlContent,
|
||||
emailSubject: this._emailSubject,
|
||||
googleAnalyticsTrackingId: this._googleAnalyticsTrackingId,
|
||||
};
|
||||
}
|
||||
}
|
106
lib/modules/invites/Invitation.js
Normal file
106
lib/modules/invites/Invitation.js
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @flow
|
||||
* Invitation representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidInvitation from './AndroidInvitation';
|
||||
|
||||
import type { NativeInvitation } from './types';
|
||||
|
||||
export default class Invitation {
|
||||
_android: AndroidInvitation;
|
||||
_androidClientId: string | void;
|
||||
_androidMinimumVersionCode: number | void;
|
||||
_callToActionText: string | void;
|
||||
_customImage: string | void;
|
||||
_deepLink: string | void;
|
||||
_iosClientId: string | void;
|
||||
_message: string;
|
||||
_title: string;
|
||||
|
||||
constructor(title: string, message: string) {
|
||||
this._android = new AndroidInvitation(this);
|
||||
this._message = message;
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidClientId(androidClientId: string): Invitation {
|
||||
this._androidClientId = androidClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidMinimumVersionCode
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidMinimumVersionCode(androidMinimumVersionCode: number): Invitation {
|
||||
this._androidMinimumVersionCode = androidMinimumVersionCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param callToActionText
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCallToActionText(callToActionText: string): Invitation {
|
||||
this._callToActionText = callToActionText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param customImage
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCustomImage(customImage: string): Invitation {
|
||||
this._customImage = customImage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param deepLink
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setDeepLink(deepLink: string): Invitation {
|
||||
this._deepLink = deepLink;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iosClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setIOSClientId(iosClientId: string): Invitation {
|
||||
this._iosClientId = iosClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeInvitation {
|
||||
if (!this._message) {
|
||||
throw new Error('Invitation: Missing required `message` property');
|
||||
} else if (!this._title) {
|
||||
throw new Error('Invitation: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
android: Platform.OS === 'android' ? this._android.build() : undefined,
|
||||
androidClientId: this._androidClientId,
|
||||
androidMinimumVersionCode: this._androidMinimumVersionCode,
|
||||
callToActionText: this._callToActionText,
|
||||
customImage: this._customImage,
|
||||
deepLink: this._deepLink,
|
||||
iosClientId: this._iosClientId,
|
||||
message: this._message,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
77
lib/modules/invites/index.js
Normal file
77
lib/modules/invites/index.js
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @flow
|
||||
* Invites representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import Invitation from './Invitation';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseInvites';
|
||||
export const NAMESPACE = 'invites';
|
||||
const NATIVE_EVENTS = ['invites_invitation_received'];
|
||||
|
||||
type InvitationOpen = {
|
||||
deepLink: string,
|
||||
invitationId: string,
|
||||
};
|
||||
|
||||
export default class Invites extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'invites_invitation_received',
|
||||
(invitation: InvitationOpen) => {
|
||||
SharedEventEmitter.emit('onInvitation', invitation);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invitation that triggered application open
|
||||
* @returns {Promise.<Object>}
|
||||
*/
|
||||
getInitialInvitation(): Promise<string> {
|
||||
return getNativeModule(this).getInitialInvitation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to invites
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onInvitation(listener: InvitationOpen => any) {
|
||||
getLogger(this).info('Creating onInvitation listener');
|
||||
|
||||
SharedEventEmitter.addListener('onInvitation', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onInvitation listener');
|
||||
SharedEventEmitter.removeListener('onInvitation', listener);
|
||||
};
|
||||
}
|
||||
|
||||
sendInvitation(invitation: Invitation): Promise<string[]> {
|
||||
if (!(invitation instanceof Invitation)) {
|
||||
throw new Error(
|
||||
`Invites:sendInvitation expects an 'Invitation' but got type ${typeof invitation}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).sendInvitation(invitation.build());
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Invitation,
|
||||
};
|
21
lib/modules/invites/types.js
Normal file
21
lib/modules/invites/types.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type NativeAndroidInvitation = {|
|
||||
additionalReferralParameters?: { [string]: string },
|
||||
emailHtmlContent?: string,
|
||||
emailSubject?: string,
|
||||
googleAnalyticsTrackingId?: string,
|
||||
|};
|
||||
|
||||
export type NativeInvitation = {|
|
||||
android?: NativeAndroidInvitation,
|
||||
androidClientId?: string,
|
||||
androidMinimumVersionCode?: number,
|
||||
callToActionText?: string,
|
||||
customImage?: string,
|
||||
deepLink?: string,
|
||||
iosClientId?: string,
|
||||
message: string,
|
||||
title: string,
|
||||
|};
|
@ -3,6 +3,7 @@
|
||||
* Dynamic Links representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { areObjectKeysContainedInOther, isObject, isString } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
@ -10,7 +11,7 @@ import { getNativeModule } from '../../utils/native';
|
||||
import type App from '../core/app';
|
||||
|
||||
const EVENT_TYPE = {
|
||||
Link: 'dynamic_link_received',
|
||||
Link: 'links_link_received',
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [EVENT_TYPE.Link];
|
||||
@ -82,33 +83,21 @@ export default class Links extends ModuleBase {
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'links_link_received',
|
||||
(link: string) => {
|
||||
SharedEventEmitter.emit('onLink', link);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get EVENT_TYPE(): Object {
|
||||
return EVENT_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link that triggered application open
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
getInitialLink(): Promise<string> {
|
||||
return getNativeModule(this).getInitialLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to dynamic links
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onLink(listener: Function): () => any {
|
||||
const rnListener = SharedEventEmitter.addListener(
|
||||
EVENT_TYPE.Link,
|
||||
listener
|
||||
);
|
||||
return () => rnListener.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create long Dynamic Link from parameters
|
||||
* @param parameters
|
||||
@ -138,6 +127,30 @@ export default class Links extends ModuleBase {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link that triggered application open
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
getInitialLink(): Promise<string> {
|
||||
return getNativeModule(this).getInitialLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to dynamic links
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onLink(listener: string => any): () => any {
|
||||
getLogger(this).info('Creating onLink listener');
|
||||
|
||||
SharedEventEmitter.addListener('onLink', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onLink listener');
|
||||
SharedEventEmitter.removeListener('onLink', listener);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
|
@ -133,13 +133,13 @@ export default class RemoteMessage {
|
||||
}
|
||||
|
||||
build(): NativeOutboundRemoteMessage {
|
||||
if (!this.data) {
|
||||
if (!this._data) {
|
||||
throw new Error('RemoteMessage: Missing required `data` property');
|
||||
} else if (!this.messageId) {
|
||||
} else if (!this._messageId) {
|
||||
throw new Error('RemoteMessage: Missing required `messageId` property');
|
||||
} else if (!this.to) {
|
||||
} else if (!this._to) {
|
||||
throw new Error('RemoteMessage: Missing required `to` property');
|
||||
} else if (!this.ttl) {
|
||||
} else if (!this._ttl) {
|
||||
throw new Error('RemoteMessage: Missing required `ttl` property');
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ import type Firestore from '../modules/firestore';
|
||||
import { typeof statics as FirestoreStatics } from '../modules/firestore';
|
||||
import type InstanceId from '../modules/instanceid';
|
||||
import { typeof statics as InstanceIdStatics } from '../modules/instanceid';
|
||||
import type Invites from '../modules/invites';
|
||||
import { typeof statics as InvitesStatics } from '../modules/invites';
|
||||
import type Links from '../modules/links';
|
||||
import { typeof statics as LinksStatics } from '../modules/links';
|
||||
import type Messaging from '../modules/messaging';
|
||||
@ -61,6 +63,7 @@ export type FirebaseModuleName =
|
||||
| 'RNFirebaseDatabase'
|
||||
| 'RNFirebaseFirestore'
|
||||
| 'RNFirebaseInstanceId'
|
||||
| 'RNFirebaseInvites'
|
||||
| 'RNFirebaseLinks'
|
||||
| 'RNFirebaseMessaging'
|
||||
| 'RNFirebaseNotifications'
|
||||
@ -78,6 +81,7 @@ export type FirebaseNamespace =
|
||||
| 'database'
|
||||
| 'firestore'
|
||||
| 'instanceid'
|
||||
| 'invites'
|
||||
| 'links'
|
||||
| 'messaging'
|
||||
| 'notifications'
|
||||
@ -173,10 +177,17 @@ export type FirestoreModule = {
|
||||
/* InstanceId types */
|
||||
|
||||
export type InstanceIdModule = {
|
||||
(): InstanceId,
|
||||
nativeModuleExists: boolean,
|
||||
(): InstanceId,
|
||||
nativeModuleExists: boolean,
|
||||
} & InstanceIdStatics;
|
||||
|
||||
/* Invites types */
|
||||
|
||||
export type InvitesModule = {
|
||||
(): Invites,
|
||||
nativeModuleExists: boolean,
|
||||
} & InvitesStatics;
|
||||
|
||||
/* Links types */
|
||||
|
||||
export type LinksModule = {
|
||||
|
@ -42,6 +42,15 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- App Links -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:host="je786.app.goo.gl" android:scheme="http"/>
|
||||
<data android:host="je786.app.goo.gl" android:scheme="https"/>
|
||||
</intent-filter>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
|
@ -13,6 +13,7 @@ import io.invertase.firebase.database.RNFirebaseDatabasePackage;
|
||||
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
|
||||
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
|
||||
import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage;
|
||||
import io.invertase.firebase.invites.RNFirebaseInvitesPackage;
|
||||
import io.invertase.firebase.links.RNFirebaseLinksPackage;
|
||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
||||
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
|
||||
@ -50,6 +51,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
new RNFirebaseDatabasePackage(),
|
||||
new RNFirebaseFirestorePackage(),
|
||||
new RNFirebaseInstanceIdPackage(),
|
||||
new RNFirebaseInvitesPackage(),
|
||||
new RNFirebaseLinksPackage(),
|
||||
new RNFirebaseMessagingPackage(),
|
||||
new RNFirebaseNotificationsPackage(),
|
||||
|
@ -14,7 +14,8 @@ RNfirebase.firestore.enableLogging(false);
|
||||
// RNfirebase.utils().logLevel = 'info';
|
||||
RNfirebase.utils().logLevel = 'warn'; // default
|
||||
|
||||
const init = async () => {
|
||||
// Messaging and Notifications testing
|
||||
const notifications = async () => {
|
||||
try {
|
||||
await RNfirebase.messaging().requestPermission();
|
||||
const instanceid = await RNfirebase.instanceid().get();
|
||||
@ -82,7 +83,9 @@ const init = async () => {
|
||||
.then(() => {
|
||||
RNfirebase.notifications()
|
||||
.getScheduledNotifications()
|
||||
.then(notifications => console.log('scheduled: ', notifications));
|
||||
.then(scheduledNotifications =>
|
||||
console.log('scheduled: ', scheduledNotifications)
|
||||
);
|
||||
});
|
||||
}, 5);
|
||||
} catch (error) {
|
||||
@ -90,7 +93,32 @@ const init = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
// notifications();
|
||||
|
||||
// Invitations testing
|
||||
const invitations = async () => {
|
||||
try {
|
||||
const initialLink = await RNfirebase.links().getInitialLink();
|
||||
console.log('initialLink: ', initialLink);
|
||||
const initialInvite = await RNfirebase.invites().getInitialInvitation();
|
||||
console.log('initialInvite: ', initialInvite);
|
||||
|
||||
RNfirebase.links().onLink(link => {
|
||||
console.log('onLink: ', link);
|
||||
});
|
||||
RNfirebase.invites().onInvitation(invite => {
|
||||
console.log('onInvitation: ', invite);
|
||||
});
|
||||
const invitation = new RNfirebase.invites.Invitation('Title', 'Message');
|
||||
invitation.setDeepLink('https://je786.app.goo.gl/testing');
|
||||
const invitationIds = await RNfirebase.invites().sendInvitation(invitation);
|
||||
console.log('InvitationIds: ', invitationIds);
|
||||
} catch (error) {
|
||||
console.error('invitations init error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
invitations();
|
||||
|
||||
const config = {
|
||||
apiKey: 'AIzaSyDnVqNhxU0Biit9nCo4RorAh5ulQQwko3E',
|
||||
|
Loading…
x
Reference in New Issue
Block a user