2
0
mirror of synced 2025-02-02 01:25:50 +00:00

[invites] Initial JS and Android invites functionality

This commit is contained in:
Chris Bianca 2018-03-22 12:46:37 +00:00
parent 26c3ed9a60
commit 53babb4cd9
16 changed files with 747 additions and 135 deletions

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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(

View File

@ -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',

View 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,
};
}
}

View 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,
};
}
}

View 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,
};

View 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,
|};

View File

@ -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 = {

View File

@ -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');
}

View File

@ -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 = {

View File

@ -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"

View File

@ -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(),

View File

@ -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',