Merge commit '49d29b53f21e530f5c918e472db93ee856947426'
This commit is contained in:
commit
51074efdca
|
@ -64,3 +64,6 @@ yarn.lock
|
|||
tests
|
||||
lib/.watchmanconfig
|
||||
buddybuild_postclone.sh
|
||||
bin/test.js
|
||||
.github
|
||||
example
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg?style=flat-square)](https://www.npmjs.com/package/react-native-firebase)
|
||||
[![NPM downloads](https://img.shields.io/npm/dm/react-native-firebase.svg?style=flat-square)](https://www.npmjs.com/package/react-native-firebase)
|
||||
[![Package Quality](http://npm.packagequality.com/shield/react-native-firebase.svg?style=flat-square)](http://packagequality.com/#?package=react-native-firebase)
|
||||
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
|
||||
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
|
||||
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
|
||||
|
||||
|
||||
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
|
||||
|
||||
<!---
|
||||
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
|
||||
-->
|
||||
|
||||
<hr>
|
||||
|
||||
> [Current Docs](http://invertase.link/docs) <b>|</b> [@next Docs](http://invertase.link/v3) <b>|</b> [iOS Install Guide](http://invertase.link/ios) <b>|</b> [Android Install Guide](http://invertase.link/android) <b>|</b> [FAQs](http://invertase.io/react-native-firebase/#/faqs) <b>|</b> [Feature Requests](http://invertase.link/requests)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.invertase.firebase.analytics;
|
||||
|
||||
import android.support.annotation.RequiresPermission;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
|
@ -13,6 +15,9 @@ import java.util.List;
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
public class RNFirebaseAnalyticsPackage implements ReactPackage {
|
||||
@RequiresPermission(
|
||||
allOf = {"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE", "android.permission.WAKE_LOCK"}
|
||||
)
|
||||
public RNFirebaseAnalyticsPackage() {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.invertase.firebase.auth;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
@ -721,6 +723,101 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* verifyPhoneNumber
|
||||
*
|
||||
* @param appName
|
||||
* @param phoneNumber
|
||||
* @param timeout
|
||||
*/
|
||||
@ReactMethod
|
||||
public void verifyPhoneNumber(final String appName, final String phoneNumber, final String requestKey, final int timeout) {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
|
||||
final Activity activity = mReactContext.getCurrentActivity();
|
||||
|
||||
Log.d(TAG, "verifyPhoneNumber:" + phoneNumber);
|
||||
|
||||
PhoneAuthProvider.OnVerificationStateChangedCallbacks callbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
|
||||
|
||||
@Override
|
||||
public void onVerificationCompleted(final PhoneAuthCredential phoneAuthCredential) {
|
||||
Log.d(TAG, "verifyPhoneNumber:verification:onVerificationCompleted");
|
||||
WritableMap state = Arguments.createMap();
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
phoneAuthCredential.writeToParcel(parcel, 0);
|
||||
|
||||
// verificationId
|
||||
parcel.setDataPosition(16);
|
||||
String verificationId = parcel.readString();
|
||||
|
||||
// sms Code
|
||||
parcel.setDataPosition(parcel.dataPosition() + 8);
|
||||
String code = parcel.readString();
|
||||
|
||||
state.putString("code", code);
|
||||
state.putString("verificationId", verificationId);
|
||||
parcel.recycle();
|
||||
sendPhoneStateEvent(appName, requestKey, "onVerificationComplete", state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVerificationFailed(FirebaseException e) {
|
||||
// This callback is invoked in an invalid request for verification is made,
|
||||
// e.g. phone number format is incorrect, or the SMS quota for the project
|
||||
// has been exceeded
|
||||
Log.d(TAG, "verifyPhoneNumber:verification:onVerificationFailed");
|
||||
WritableMap state = Arguments.createMap();
|
||||
state.putMap("error", getJSError(e));
|
||||
sendPhoneStateEvent(appName, requestKey, "onVerificationFailed", state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
|
||||
Log.d(TAG, "verifyPhoneNumber:verification:onCodeSent");
|
||||
WritableMap state = Arguments.createMap();
|
||||
state.putString("verificationId", verificationId);
|
||||
|
||||
// todo forceResendingToken - it's actually just an empty class ... no actual token >.>
|
||||
// Parcel parcel = Parcel.obtain();
|
||||
// forceResendingToken.writeToParcel(parcel, 0);
|
||||
//
|
||||
// // verificationId
|
||||
// parcel.setDataPosition(0);
|
||||
// int int1 = parcel.readInt();
|
||||
// String token = parcel.readString();
|
||||
//
|
||||
// state.putString("refreshToken", token);
|
||||
// parcel.recycle();
|
||||
|
||||
state.putString("verificationId", verificationId);
|
||||
sendPhoneStateEvent(appName, requestKey, "onCodeSent", state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCodeAutoRetrievalTimeOut(String verificationId) {
|
||||
super.onCodeAutoRetrievalTimeOut(verificationId);
|
||||
Log.d(TAG, "verifyPhoneNumber:verification:onCodeAutoRetrievalTimeOut");
|
||||
WritableMap state = Arguments.createMap();
|
||||
state.putString("verificationId", verificationId);
|
||||
sendPhoneStateEvent(appName, requestKey, "onCodeAutoRetrievalTimeout", state);
|
||||
}
|
||||
};
|
||||
|
||||
if (activity != null) {
|
||||
PhoneAuthProvider.getInstance(firebaseAuth)
|
||||
.verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
timeout,
|
||||
TimeUnit.SECONDS,
|
||||
activity,
|
||||
callbacks
|
||||
//, PhoneAuthProvider.ForceResendingToken.zzboe() // TODO FORCE RESENDING
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* confirmPasswordReset
|
||||
*
|
||||
|
@ -1270,4 +1367,19 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
|
||||
return userMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param requestKey
|
||||
* @param type
|
||||
* @param state
|
||||
*/
|
||||
private void sendPhoneStateEvent(String appName, String requestKey, String type, WritableMap state) {
|
||||
WritableMap eventMap = Arguments.createMap();
|
||||
eventMap.putString("appName", appName);
|
||||
eventMap.putString("requestKey", requestKey);
|
||||
eventMap.putString("type", type);
|
||||
eventMap.putMap("state", state);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_state_changed", eventMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.google.firebase.messaging.RemoteMessage.Notification;
|
|||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -72,6 +73,18 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L
|
|||
promise.resolve(FirebaseInstanceId.getInstance().getToken());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deleteInstanceId(Promise promise){
|
||||
try {
|
||||
Log.d(TAG, "Deleting instance id");
|
||||
FirebaseInstanceId.getInstance().deleteInstanceId();
|
||||
promise.resolve(null);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
promise.reject(null, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createLocalNotification(ReadableMap details) {
|
||||
Bundle bundle = Arguments.toBundle(details);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.invertase.firebase.storage;
|
||||
|
||||
import android.support.annotation.RequiresPermission;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
|
@ -13,6 +15,9 @@ import java.util.List;
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
public class RNFirebaseStoragePackage implements ReactPackage {
|
||||
@RequiresPermission(
|
||||
allOf = {"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"}
|
||||
)
|
||||
public RNFirebaseStoragePackage() {
|
||||
}
|
||||
|
||||
|
@ -35,7 +40,7 @@ public class RNFirebaseStoragePackage implements ReactPackage {
|
|||
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
|
||||
* will be automatically included in the JS bundle.
|
||||
*/
|
||||
// TODO: Removed in 0.47.0. Here for backwards compatability
|
||||
// TODO: Removed in 0.47.0. Here for backwards compatibility
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
const shell = require('shelljs');
|
||||
|
||||
const WATCH_SRC = require('path').resolve('./lib');
|
||||
const WATCH_OUT = require('path').resolve('./tests/firebase');
|
||||
|
||||
/*
|
||||
"tests-watch-init": "wml add $(node --eval \"console.log(require('path').resolve('./lib'));\") $(node --eval \"console.log(require('path').resolve('./tests/firebase'));\")",
|
||||
"tests-watch-start": "watchman watch $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml start",
|
||||
"tests-watch-stop": "watchman watch-del $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml stop"
|
||||
*/
|
||||
|
||||
if (process.argv.includes('watch')) {
|
||||
if (!shell.which('wml')) {
|
||||
shell.echo('');
|
||||
shell.echo('---------------------------------------------------');
|
||||
shell.echo(' Missing required npm global from library wix/wml. ');
|
||||
shell.echo('---------------------------------------------------');
|
||||
shell.echo('');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (!shell.which('watchman')) {
|
||||
shell.echo('');
|
||||
shell.echo('---------------------------------------------------');
|
||||
shell.echo(' Missing required executable: watchman ');
|
||||
shell.echo('---------------------------------------------------');
|
||||
shell.echo('');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (process.argv.includes('init')) {
|
||||
console.log(`wml add ${WATCH_SRC} ${WATCH_OUT}`);
|
||||
if (shell.exec(`wml add ${WATCH_SRC} ${WATCH_OUT}`).code !== 0) {
|
||||
shell.echo('Error setting up watched location via WML.');
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv.includes('start')) {
|
||||
console.log(`watchman watch ${WATCH_SRC} && wml start`);
|
||||
const watcher = shell.exec(`watchman watch ${WATCH_SRC} && wml start`, { async: true });
|
||||
watcher.stdout.on('data', console.log);
|
||||
watcher.stderr.on('data', console.error);
|
||||
}
|
||||
|
||||
if (process.argv.includes('stop')) {
|
||||
console.log(`watchman watch-del ${WATCH_SRC} && wml stop && wml rm all`);
|
||||
const watcher = shell.exec(`watchman watch-del ${WATCH_SRC} && wml stop && wml rm all`, { async: true });
|
||||
watcher.stdout.on('data', console.log);
|
||||
watcher.stderr.on('data', console.error);
|
||||
}
|
||||
}
|
|
@ -53,6 +53,20 @@ firebase.messaging().getToken()
|
|||
});
|
||||
```
|
||||
|
||||
### deleteInstanceId(): `Promise<any>`
|
||||
|
||||
Reset Instance ID and revokes all tokens.
|
||||
|
||||
```javascript
|
||||
firebase.messaging().deleteInstanceId()
|
||||
.then(() => {
|
||||
console.log('Deleted instance id successfully');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(`Cannot delete instance id: ${error.message}`);
|
||||
});
|
||||
```
|
||||
|
||||
### onTokenRefresh(listener: `Function<string>`)
|
||||
|
||||
On the event a devices FCM token is refreshed by Google, the new token is returned in a callback listener.
|
||||
|
|
|
@ -8,7 +8,7 @@ declare module "react-native-firebase" {
|
|||
export default class FireBase {
|
||||
constructor(config?: RNFirebase.configurationOptions)
|
||||
|
||||
log: any
|
||||
log: any;
|
||||
|
||||
analytics(): RNFirebase.Analytics;
|
||||
|
||||
|
@ -22,7 +22,7 @@ declare module "react-native-firebase" {
|
|||
ServerValue: {
|
||||
TIMESTAMP: number
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**RNFirebase mimics the Web Firebase SDK Storage,
|
||||
* whilst providing some iOS and Android specific functionality.
|
||||
|
@ -608,6 +608,10 @@ declare module "react-native-firebase" {
|
|||
* This token can be used in the Firebase console to send messages to directly.
|
||||
*/
|
||||
getToken(forceRefresh?: Boolean): Promise<string>
|
||||
/**
|
||||
* Reset Instance ID and revokes all tokens.
|
||||
*/
|
||||
deleteInstanceId(): Promise<any>
|
||||
/**
|
||||
* On the event a devices FCM token is refreshed by Google,
|
||||
* the new token is returned in a callback listener.
|
||||
|
|
|
@ -79,7 +79,7 @@ RCT_EXPORT_METHOD(deleteApp:
|
|||
|
||||
/**
|
||||
* React native constant exports - exports native firebase apps mainly
|
||||
* @return
|
||||
* @return NSDictionary
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport {
|
||||
NSMutableDictionary *constants = [NSMutableDictionary new];
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
static NSString *const AUTH_CHANGED_EVENT = @"auth_state_changed";
|
||||
static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
|
||||
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed";
|
||||
|
||||
// Database
|
||||
static NSString *const DATABASE_SYNC_EVENT = @"database_sync_event";
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(log:(NSString *)message) {
|
||||
FIRCrashLog(message);
|
||||
FIRCrashLog(@"%@", message);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(logcat:(nonnull NSNumber *) level tag:(NSString *) tag message:(NSString *) message) {
|
||||
FIRCrashLog(message);
|
||||
FIRCrashLog(@"%@", message);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(report:(NSString *) message) {
|
||||
FIRCrashLog(message);
|
||||
FIRCrashLog(@"%@", message);
|
||||
assert(NO);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||
#import <FirebaseInstanceID/FIRInstanceID.h>
|
||||
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTConvert.h>
|
||||
|
@ -257,6 +258,16 @@ RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseR
|
|||
resolve([FIRMessaging messaging].FCMToken);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
[[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
} else {
|
||||
reject(@"instance_id_error", @"Failed to delete instance id", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (RCTRunningInAppExtension()) {
|
||||
return;
|
||||
|
|
|
@ -221,8 +221,22 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
|
|||
options.networkAccessAllowed = true;
|
||||
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
||||
if (info[PHImageErrorKey] == nil) {
|
||||
firmetadata.contentType = [self utiToMimeType:dataUTI];
|
||||
[self uploadData:appName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
|
||||
if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) {
|
||||
firmetadata.contentType = [self utiToMimeType:dataUTI];
|
||||
[self uploadData:appName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
|
||||
} else {
|
||||
// if the image UTI is not JPEG then convert to JPEG, e.g. HEI
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
|
||||
NSDictionary *imageInfo = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
|
||||
NSDictionary *imageMetadata = [imageInfo copy];
|
||||
NSMutableData *imageDataJPEG = [NSMutableData data];
|
||||
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataJPEG, kUTTypeJPEG, 1, NULL);
|
||||
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)imageMetadata);
|
||||
CGImageDestinationFinalize(destination);
|
||||
// Manually set mimetype to JPEG
|
||||
firmetadata.contentType = @"image/jpeg";
|
||||
[self uploadData:appName data:[NSData dataWithData:imageDataJPEG] firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
|
||||
}
|
||||
} else {
|
||||
reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import INTERNALS from './../../internals';
|
||||
import { generatePushID, isFunction, isAndroid, isIOS, isString } from './../../utils';
|
||||
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from './../../utils';
|
||||
|
||||
type PhoneAuthSnapshot = {
|
||||
state: 'sent' | 'timeout' | 'verified' | 'error',
|
||||
|
@ -17,15 +17,16 @@ type PhoneAuthError = {
|
|||
};
|
||||
|
||||
export default class PhoneAuthListener {
|
||||
|
||||
_auth: Object;
|
||||
_reject: Function | null;
|
||||
_resolve: Function | null;
|
||||
_promise: Promise | null;
|
||||
_credential: Object | null;
|
||||
_timeout: number;
|
||||
_phoneAuthRequestKey: string;
|
||||
_publicEvents: Object;
|
||||
_internalEvents: Object;
|
||||
_reject: Function | null;
|
||||
_resolve: Function | null;
|
||||
_credential: Object | null;
|
||||
_promise: Promise<*> | null;
|
||||
_phoneAuthRequestKey: string;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -33,14 +34,14 @@ export default class PhoneAuthListener {
|
|||
* @param phoneNumber
|
||||
* @param timeout
|
||||
*/
|
||||
constructor(auth: Object, phoneNumber: string, timeout): PhoneAuthListener {
|
||||
constructor(auth: Object, phoneNumber: string, timeout?: number) {
|
||||
this._auth = auth;
|
||||
this._reject = null;
|
||||
this._resolve = null;
|
||||
this._promise = null;
|
||||
this._credential = null;
|
||||
|
||||
this._timeout = timeout || 20000; // 20 secs
|
||||
this._timeout = timeout || 20; // 20 secs
|
||||
this._phoneAuthRequestKey = generatePushID();
|
||||
|
||||
// internal events
|
||||
|
@ -65,11 +66,20 @@ export default class PhoneAuthListener {
|
|||
this._subscribeToEvents();
|
||||
|
||||
// start verification flow natively
|
||||
this._auth._native.verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey,
|
||||
this._timeout,
|
||||
);
|
||||
if (isAndroid) {
|
||||
this._auth._native.verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey,
|
||||
this._timeout,
|
||||
);
|
||||
}
|
||||
|
||||
if (isIOS) {
|
||||
this._auth._native.verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,8 +120,6 @@ export default class PhoneAuthListener {
|
|||
*/
|
||||
_emitToErrorCb(snapshot) {
|
||||
const error = snapshot.error;
|
||||
error.verificationId = snapshot.verificationId;
|
||||
|
||||
if (this._reject) this._reject(error);
|
||||
this._auth.emit(this._publicEvents.error, error);
|
||||
}
|
||||
|
@ -174,12 +182,12 @@ export default class PhoneAuthListener {
|
|||
|
||||
/**
|
||||
* Internal code sent event handler
|
||||
* @param verificationId
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeSentHandler(verificationId) {
|
||||
_codeSentHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId,
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'sent',
|
||||
|
@ -199,12 +207,12 @@ export default class PhoneAuthListener {
|
|||
|
||||
/**
|
||||
* Internal code auto retrieve timeout event handler
|
||||
* @param verificationId
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeAutoRetrievalTimeoutHandler(verificationId) {
|
||||
_codeAutoRetrievalTimeoutHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId,
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'timeout',
|
||||
|
@ -234,17 +242,19 @@ export default class PhoneAuthListener {
|
|||
|
||||
/**
|
||||
* Internal verification failed event handler
|
||||
* @param errObject
|
||||
* @param state
|
||||
* @private
|
||||
*/
|
||||
_verificationFailedHandler(errObject) {
|
||||
_verificationFailedHandler(state) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: errObject.verificationId,
|
||||
verificationId: state.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'error',
|
||||
};
|
||||
|
||||
const { code, message, nativeErrorMessage } = state.error;
|
||||
snapshot.error = nativeToJSError(code, message, { nativeErrorMessage });
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToErrorCb(snapshot);
|
||||
|
@ -256,7 +266,7 @@ export default class PhoneAuthListener {
|
|||
-- PUBLIC API
|
||||
--------------*/
|
||||
|
||||
on(event: string, observer: () => PhoneAuthSnapshot, errorCb?: () => PhoneAuthError, successCb?: () => PhoneAuthSnapshot) {
|
||||
on(event: string, observer: () => PhoneAuthSnapshot, errorCb?: () => PhoneAuthError, successCb?: () => PhoneAuthSnapshot): this {
|
||||
if (!isString(event)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on'));
|
||||
}
|
||||
|
@ -286,17 +296,19 @@ export default class PhoneAuthListener {
|
|||
* Promise .then proxy
|
||||
* @param fn
|
||||
*/
|
||||
then(fn) {
|
||||
then(fn: () => PhoneAuthSnapshot) {
|
||||
this._promiseDeferred();
|
||||
return this._promise.then.bind(this._promise)(fn);
|
||||
if (this._promise) return this._promise.then.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise .catch proxy
|
||||
* @param fn
|
||||
*/
|
||||
catch(fn) {
|
||||
catch(fn: () => Error) {
|
||||
this._promiseDeferred();
|
||||
return this._promise.catch.bind(this._promise)(fn);
|
||||
if (this._promise) return this._promise.catch.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ export default class Auth extends ModuleBase {
|
|||
static _NATIVE_MODULE = 'RNFirebaseAuth';
|
||||
|
||||
_user: User | null;
|
||||
_native: Object;
|
||||
_getAppEventName: Function;
|
||||
_authResult: AuthResultType | null;
|
||||
authenticated: boolean;
|
||||
|
||||
|
@ -35,6 +37,13 @@ export default class Auth extends ModuleBase {
|
|||
this._onAuthStateChanged.bind(this),
|
||||
);
|
||||
|
||||
this.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public events based on event.type
|
||||
this._getAppEventName('phone_auth_state_changed'),
|
||||
this._onPhoneAuthStateChanged.bind(this),
|
||||
);
|
||||
|
||||
this.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onIdTokenChanged
|
||||
|
@ -46,6 +55,16 @@ export default class Auth extends ModuleBase {
|
|||
this._native.addIdTokenListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Route a phone state change event to the correct listeners
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onPhoneAuthStateChanged(event: Object) {
|
||||
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
|
||||
this.emit(eventKey, event.state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal auth changed listener
|
||||
* @param auth
|
||||
|
@ -100,7 +119,7 @@ export default class Auth extends ModuleBase {
|
|||
* Intercept all user actions and send their results to
|
||||
* auth state change before resolving
|
||||
* @param promise
|
||||
* @returns {Promise.<TResult>|*}
|
||||
* @returns {Promise.<*>}
|
||||
* @private
|
||||
*/
|
||||
_interceptUserValue(promise) {
|
||||
|
@ -234,7 +253,7 @@ export default class Auth extends ModuleBase {
|
|||
* @param newPassword
|
||||
* @return {Promise.<Null>}
|
||||
*/
|
||||
confirmPasswordReset(code: string, newPassword: string): Promise<Null> {
|
||||
confirmPasswordReset(code: string, newPassword: string): Promise<null> {
|
||||
return this._native.confirmPasswordReset(code, newPassword);
|
||||
}
|
||||
|
||||
|
@ -245,7 +264,7 @@ export default class Auth extends ModuleBase {
|
|||
* @param code
|
||||
* @return {Promise.<Null>}
|
||||
*/
|
||||
applyActionCode(code: string): Promise<Any> {
|
||||
applyActionCode(code: string): Promise<any> {
|
||||
return this._native.applyActionCode(code);
|
||||
}
|
||||
|
||||
|
@ -254,9 +273,9 @@ export default class Auth extends ModuleBase {
|
|||
*
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#checkActionCode
|
||||
* @param code
|
||||
* @return {Promise.<Any>|Promise<ActionCodeInfo>}
|
||||
* @return {Promise.<any>|Promise<ActionCodeInfo>}
|
||||
*/
|
||||
checkActionCode(code: string): Promise<Any> {
|
||||
checkActionCode(code: string): Promise<any> {
|
||||
return this._native.checkActionCode(code);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Platform } from 'react-native';
|
||||
import { Platform, NativeModules } from 'react-native';
|
||||
import ModuleBase from './../../utils/ModuleBase';
|
||||
import RemoteMessage from './RemoteMessage';
|
||||
|
||||
|
@ -25,6 +25,8 @@ const WILL_PRESENT_RESULT = {
|
|||
None: 'UNNotificationPresentationOptionNone',
|
||||
};
|
||||
|
||||
const FirebaseMessaging = NativeModules.FirebaseMessaging;
|
||||
|
||||
/**
|
||||
* IOS only finish function
|
||||
* @param data
|
||||
|
@ -109,6 +111,14 @@ export default class Messaging extends ModuleBase {
|
|||
return this._native.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Instance ID and revokes all tokens.
|
||||
* @returns {*|Promise.<*>}
|
||||
*/
|
||||
deleteInstanceId() {
|
||||
return this._native.deleteInstanceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and display a local notification
|
||||
* @param notification
|
||||
|
|
|
@ -6,6 +6,8 @@ export default class PerformanceMonitoring extends ModuleBase {
|
|||
static _NAMESPACE = 'perf';
|
||||
static _NATIVE_MODULE = 'RNFirebasePerformance';
|
||||
|
||||
_native: Object;
|
||||
|
||||
constructor(firebaseApp: Object, options: Object = {}) {
|
||||
super(firebaseApp, options);
|
||||
}
|
||||
|
@ -26,8 +28,4 @@ export default class PerformanceMonitoring extends ModuleBase {
|
|||
newTrace(trace: string): void {
|
||||
return new Trace(this, trace);
|
||||
}
|
||||
|
||||
get namespace(): string {
|
||||
return 'firebase:perf';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
* @flow
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import Log from '../utils/log';
|
||||
import { nativeWithApp } from './../utils';
|
||||
import INTERNALS from './../internals';
|
||||
import FirebaseCore from './../firebase';
|
||||
import FirebaseApp from '../firebase-app';
|
||||
import { nativeWithApp } from './../utils';
|
||||
|
||||
const logs = {};
|
||||
|
||||
|
@ -24,6 +26,7 @@ const NATIVE_MODULE_EVENTS = {
|
|||
],
|
||||
Auth: [
|
||||
'auth_state_changed',
|
||||
'phone_auth_state_changed',
|
||||
],
|
||||
Database: [
|
||||
'database_transaction_event',
|
||||
|
@ -42,14 +45,23 @@ const DEFAULTS = {
|
|||
};
|
||||
|
||||
export default class ModuleBase {
|
||||
_native: Object;
|
||||
_module: string;
|
||||
_options: Object;
|
||||
_appName: string;
|
||||
_namespace: string;
|
||||
_firebaseApp: Object;
|
||||
_eventEmitter: Object;
|
||||
static _NAMESPACE: string;
|
||||
static _NATIVE_MODULE: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param firebaseApp
|
||||
* @param options
|
||||
* @param moduleName
|
||||
* @param withEventEmitter
|
||||
*/
|
||||
constructor(firebaseApp, options, withEventEmitter = false) {
|
||||
constructor(firebaseApp: Object, options: Object, withEventEmitter: boolean = false) {
|
||||
this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', '');
|
||||
this._firebaseApp = firebaseApp;
|
||||
this._appName = firebaseApp._name;
|
||||
|
@ -113,7 +125,7 @@ export default class ModuleBase {
|
|||
* Returns the FirebaseApp instance for current module
|
||||
* @return {*}
|
||||
*/
|
||||
get app() {
|
||||
get app(): FirebaseApp {
|
||||
return this._firebaseApp;
|
||||
}
|
||||
|
||||
|
@ -128,27 +140,27 @@ export default class ModuleBase {
|
|||
* Proxy functions to shared event emitter instance
|
||||
* https://github.com/facebook/react-native/blob/master/Libraries/EventEmitter/EventEmitter.js
|
||||
*/
|
||||
get sharedEventEmitter() {
|
||||
get sharedEventEmitter(): Object {
|
||||
return INTERNALS.SharedEventEmitter;
|
||||
}
|
||||
|
||||
get addListener() {
|
||||
get addListener(): Function {
|
||||
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get once() {
|
||||
get once(): Function {
|
||||
return INTERNALS.SharedEventEmitter.once.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get on() {
|
||||
get on(): Function {
|
||||
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get emit() {
|
||||
get emit(): Function {
|
||||
return INTERNALS.SharedEventEmitter.emit.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get listeners() {
|
||||
get listeners(): Function {
|
||||
return INTERNALS.SharedEventEmitter.listeners.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
|
@ -157,11 +169,11 @@ export default class ModuleBase {
|
|||
return subscriptions && subscriptions.length;
|
||||
}
|
||||
|
||||
get removeListener() {
|
||||
get removeListener(): Function {
|
||||
return INTERNALS.SharedEventEmitter.removeListener.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get removeAllListeners() {
|
||||
get removeAllListeners(): Function {
|
||||
return INTERNALS.SharedEventEmitter.removeAllListeners.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ import { Platform } from 'react-native';
|
|||
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
||||
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const DEFAULT_CHUNK_SIZE = 50;
|
||||
|
||||
// const DEFAULT_CHUNK_SIZE = 50;
|
||||
|
||||
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
|
||||
const REGEXP_FIELD_NAME = new RegExp(
|
||||
|
@ -92,7 +93,7 @@ export function isFunction(item?: any): boolean {
|
|||
* @param value
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function isString(value): Boolean {
|
||||
export function isString(value: any): boolean {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
|
@ -148,63 +149,63 @@ export function noop(): void {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delays chunks based on sizes per event loop.
|
||||
* @param collection
|
||||
* @param chunkSize
|
||||
* @param operation
|
||||
* @param callback
|
||||
* @private
|
||||
*/
|
||||
function _delayChunk(collection: Array<*>,
|
||||
chunkSize: number,
|
||||
operation: Function,
|
||||
callback: Function): void {
|
||||
const length = collection.length;
|
||||
const iterations = Math.ceil(length / chunkSize);
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
let thisIteration = 0;
|
||||
|
||||
setImmediate(function next() {
|
||||
const start = thisIteration * chunkSize;
|
||||
const _end = start + chunkSize;
|
||||
const end = _end >= length ? length : _end;
|
||||
const result = operation(collection.slice(start, end), start, end);
|
||||
|
||||
if (thisIteration++ > iterations) {
|
||||
callback(null, result);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Async each with optional chunk size limit
|
||||
* @param array
|
||||
* @param chunkSize
|
||||
* @param iterator
|
||||
* @param cb
|
||||
*/
|
||||
export function each(array: Array<*>,
|
||||
chunkSize: number | Function,
|
||||
iterator: Function,
|
||||
cb?: Function): void {
|
||||
if (typeof chunkSize === 'function') {
|
||||
cb = iterator;
|
||||
iterator = chunkSize;
|
||||
chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
_delayChunk(array, chunkSize, (slice, start) => {
|
||||
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
|
||||
iterator(slice[ii], start + ii);
|
||||
}
|
||||
}, cb);
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * Delays chunks based on sizes per event loop.
|
||||
// * @param collection
|
||||
// * @param chunkSize
|
||||
// * @param operation
|
||||
// * @param callback
|
||||
// * @private
|
||||
// */
|
||||
// function _delayChunk(collection: Array<*>,
|
||||
// chunkSize: number,
|
||||
// operation: Function,
|
||||
// callback: Function): void {
|
||||
// const length = collection.length;
|
||||
// const iterations = Math.ceil(length / chunkSize);
|
||||
//
|
||||
// // noinspection ES6ConvertVarToLetConst
|
||||
// let thisIteration = 0;
|
||||
//
|
||||
// setImmediate(function next() {
|
||||
// const start = thisIteration * chunkSize;
|
||||
// const _end = start + chunkSize;
|
||||
// const end = _end >= length ? length : _end;
|
||||
// const result = operation(collection.slice(start, end), start, end);
|
||||
//
|
||||
// if (thisIteration++ > iterations) {
|
||||
// callback(null, result);
|
||||
// } else {
|
||||
// setImmediate(next);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Async each with optional chunk size limit
|
||||
// * @param array
|
||||
// * @param chunkSize
|
||||
// * @param iterator
|
||||
// * @param cb
|
||||
// */
|
||||
// export function each(array: Array<*>,
|
||||
// chunkSize: number | Function,
|
||||
// iterator: Function,
|
||||
// cb?: Function): void {
|
||||
// if (typeof chunkSize === 'function') {
|
||||
// cb = iterator;
|
||||
// iterator = chunkSize;
|
||||
// chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
// }
|
||||
//
|
||||
// if (cb) {
|
||||
// _delayChunk(array, chunkSize, (slice, start) => {
|
||||
// for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
|
||||
// iterator(slice[ii], start + ii);
|
||||
// }
|
||||
// }, cb);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a string typeof that's valid for Firebase usage
|
||||
|
@ -217,41 +218,41 @@ export function typeOf(value: any): string {
|
|||
return typeof value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async map with optional chunk size limit
|
||||
* @param array
|
||||
* @param chunkSize
|
||||
* @param iterator
|
||||
* @param cb
|
||||
* @returns {*}
|
||||
*/
|
||||
export function map(array: Array<*>,
|
||||
chunkSize: number | Function,
|
||||
iterator: Function,
|
||||
cb?: Function): void {
|
||||
if (typeof chunkSize === 'function') {
|
||||
cb = iterator;
|
||||
iterator = chunkSize;
|
||||
chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
}
|
||||
// /**
|
||||
// * Async map with optional chunk size limit
|
||||
// * @param array
|
||||
// * @param chunkSize
|
||||
// * @param iterator
|
||||
// * @param cb
|
||||
// * @returns {*}
|
||||
// */
|
||||
// export function map(array: Array<*>,
|
||||
// chunkSize: number | Function,
|
||||
// iterator: Function,
|
||||
// cb?: Function): void {
|
||||
// if (typeof chunkSize === 'function') {
|
||||
// cb = iterator;
|
||||
// iterator = chunkSize;
|
||||
// chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
// }
|
||||
//
|
||||
// const result = [];
|
||||
// _delayChunk(array, chunkSize, (slice, start) => {
|
||||
// for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
|
||||
// result.push(iterator(slice[ii], start + ii, array));
|
||||
// }
|
||||
// return result;
|
||||
// }, () => cb && cb(result));
|
||||
// }
|
||||
|
||||
const result = [];
|
||||
_delayChunk(array, chunkSize, (slice, start) => {
|
||||
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
|
||||
result.push(iterator(slice[ii], start + ii, array));
|
||||
}
|
||||
return result;
|
||||
}, () => cb && cb(result));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string
|
||||
* @return {string}
|
||||
*/
|
||||
export function capitalizeFirstLetter(string: String) {
|
||||
return `${string.charAt(0).toUpperCase()}${string.slice(1)}`;
|
||||
}
|
||||
// /**
|
||||
// *
|
||||
// * @param string
|
||||
// * @return {string}
|
||||
// */
|
||||
// export function capitalizeFirstLetter(string: String) {
|
||||
// return `${string.charAt(0).toUpperCase()}${string.slice(1)}`;
|
||||
// }
|
||||
|
||||
// timestamp of last push, used to prevent local collisions if you push twice in one ms.
|
||||
let lastPushTime = 0;
|
||||
|
@ -315,7 +316,7 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
|
|||
* @returns {Error}
|
||||
*/
|
||||
export function nativeToJSError(code: string, message: string, additionalProps?: Object = {}) {
|
||||
const error = new Error(message);
|
||||
const error: Object = new Error(message);
|
||||
error.code = code;
|
||||
Object.assign(error, additionalProps);
|
||||
// exclude this function from the stack
|
||||
|
@ -329,7 +330,7 @@ export function nativeToJSError(code: string, message: string, additionalProps?:
|
|||
* @param appName
|
||||
* @param NativeModule
|
||||
*/
|
||||
export function nativeWithApp(appName, NativeModule) {
|
||||
export function nativeWithApp(appName: string, NativeModule: Object) {
|
||||
const native = {};
|
||||
const methods = Object.keys(NativeModule);
|
||||
|
||||
|
@ -348,7 +349,7 @@ export function nativeWithApp(appName, NativeModule) {
|
|||
* @param object
|
||||
* @return {string}
|
||||
*/
|
||||
export function objectToUniqueId(object: Object): String {
|
||||
export function objectToUniqueId(object: Object): string {
|
||||
if (!isObject(object) || object === null) return JSON.stringify(object);
|
||||
|
||||
const keys = Object.keys(object).sort();
|
||||
|
@ -374,7 +375,7 @@ export function objectToUniqueId(object: Object): String {
|
|||
* @param optionalCallback
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function promiseOrCallback(promise: Promise, optionalCallback?: Function) {
|
||||
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function) {
|
||||
if (!isFunction(optionalCallback)) return promise;
|
||||
|
||||
return promise.then((result) => {
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
"build": "./node_modules/.bin/babel --source-maps=true --out-dir=dist .",
|
||||
"publish_pages": "gh-pages -d public/",
|
||||
"docs-serve-local": "docsify serve docs",
|
||||
"test-cli": "node ./bin/test.js",
|
||||
"tests-packager": "cd tests && npm run start",
|
||||
"tests-npm-install": "cd tests && npm install",
|
||||
"tests-pod-install": "cd tests && npm run ios:pod:install",
|
||||
"tests-watch-init": "wml add $(node --eval \"console.log(require('path').resolve('./lib'));\") $(node --eval \"console.log(require('path').resolve('./tests/firebase'));\")",
|
||||
"tests-watch-start": "watchman watch $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml start",
|
||||
"tests-watch-stop": "watchman watch-del $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml stop"
|
||||
"tests-watch-start": "npm run test-cli watch init start",
|
||||
"tests-watch-stop": "npm run test-cli watch stop"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -74,6 +74,7 @@
|
|||
"react": "^15.3.0",
|
||||
"react-dom": "^15.3.0",
|
||||
"react-native": "^0.44.0",
|
||||
"shelljs": "^0.7.8",
|
||||
"wml": "0.0.82"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
{
|
||||
"presets": [
|
||||
"react-native"
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"development": {
|
||||
"plugins": [
|
||||
["istanbul", {
|
||||
"include": [
|
||||
"**/firebase/**.js"
|
||||
]
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,80 @@
|
|||
package com.reactnativefirebasedemo;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "ReactNativeFirebaseDemo";
|
||||
public static final int PERMISSION_REQ_CODE = 1234;
|
||||
public static final int OVERLAY_PERMISSION_REQ_CODE = 1235;
|
||||
|
||||
String[] perms = {
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "ReactNativeFirebaseDemo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
checkWindowPerms();
|
||||
}
|
||||
|
||||
public void checkWindowPerms() {
|
||||
// Checking if device version > 22 and we need to use new permission model
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
// Checking if we can draw window overlay
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
// Requesting permission for window overlay(needed for all react-native apps)
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
|
||||
}
|
||||
for (String perm : perms) {
|
||||
// Checking each permission and if denied then requesting permissions
|
||||
if (checkSelfPermission(perm) == PackageManager.PERMISSION_DENIED) {
|
||||
requestPermissions(perms, PERMISSION_REQ_CODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Window overlay permission intent result
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
|
||||
checkWindowPerms();
|
||||
}
|
||||
}
|
||||
|
||||
// Permission results
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {
|
||||
switch (permsRequestCode) {
|
||||
case PERMISSION_REQ_CODE:
|
||||
// example how to get result of permissions requests (there can be more then one permission dialog)
|
||||
// boolean readAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
|
||||
// boolean writeAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
|
||||
// checking permissions to prevent situation when user denied some permission
|
||||
checkWindowPerms();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,8 +32,9 @@
|
|||
"js-beautify": "^1.6.11",
|
||||
"lodash.groupby": "^4.6.0",
|
||||
"lodash.some": "^4.6.0",
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-native": "^0.44.0",
|
||||
"react": "16.0.0-alpha.12",
|
||||
"react-native": "^0.48.4",
|
||||
"react-test-renderer": "16.0.0-alpha.12",
|
||||
"react-native-simple-toast": "0.0.5",
|
||||
"react-native-vector-icons": "^4.0.0",
|
||||
"react-navigation": "^1.0.0-beta.9",
|
||||
|
@ -51,6 +52,7 @@
|
|||
"babel-eslint": "^7.1.1",
|
||||
"babel-jest": "19.0.0",
|
||||
"babel-plugin-flow-react-proptypes": "^0.21.0",
|
||||
"babel-plugin-istanbul": "^4.1.5",
|
||||
"babel-preset-react-native": "1.9.1",
|
||||
"colors": "^1.1.2",
|
||||
"eslint": "^3.16.1",
|
||||
|
@ -60,7 +62,6 @@
|
|||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-react": "^6.10.0",
|
||||
"jest": "19.0.2",
|
||||
"react-test-renderer": "~15.4.1",
|
||||
"redux-immutable-state-invariant": "^1.2.4"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
import React, { Component } from 'react';
|
||||
import { View, Button, Text, TextInput, Image, ActivityIndicator, Platform } from 'react-native';
|
||||
import fb from './firebase';
|
||||
|
||||
const firebase = fb.native;
|
||||
|
||||
const imageUrl = 'https://www.shareicon.net/data/512x512/2016/07/19/798524_sms_512x512.png';
|
||||
|
||||
export default class PhoneAuth extends Component {
|
||||
static getDefaultState() {
|
||||
return {
|
||||
message: '',
|
||||
error: '',
|
||||
codeInput: '',
|
||||
phoneNumber: '+44',
|
||||
auto: Platform.OS === 'android',
|
||||
autoVerifyCountDown: 0,
|
||||
sent: false,
|
||||
started: false,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.timeout = 20;
|
||||
this._autoVerifyInterval = null;
|
||||
this.state = PhoneAuth.getDefaultState();
|
||||
}
|
||||
|
||||
_tick() {
|
||||
this.setState({
|
||||
autoVerifyCountDown: this.state.autoVerifyCountDown - 1,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when confirm code is pressed - we should have the code and verificationId now in state.
|
||||
*/
|
||||
afterVerify = () => {
|
||||
const { codeInput, verificationId } = this.state;
|
||||
const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, codeInput);
|
||||
|
||||
// TODO do something with credential for example:
|
||||
firebase.auth()
|
||||
.signInWithCredential(credential)
|
||||
.then((user) => {
|
||||
console.log('PHONE AUTH USER ->>>>>', user.toJSON());
|
||||
this.setState({ user: user.toJSON() });
|
||||
}).catch(console.error);
|
||||
};
|
||||
|
||||
signIn = () => {
|
||||
const { phoneNumber } = this.state;
|
||||
this.setState({ message: 'Sending code ...', error: '', started: true, autoVerifyCountDown: this.timeout }, () => {
|
||||
firebase.auth()
|
||||
.verifyPhoneNumber(phoneNumber)
|
||||
.on('state_changed', (phoneAuthSnapshot) => {
|
||||
console.log(phoneAuthSnapshot);
|
||||
switch (phoneAuthSnapshot.state) {
|
||||
case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
|
||||
// update state with code sent and if android start a interval timer
|
||||
// for auto verify - to provide visual feedback
|
||||
this.setState({
|
||||
sent: true,
|
||||
message: 'Code Sent!',
|
||||
verificationId: phoneAuthSnapshot.verificationId,
|
||||
autoVerifyCountDown: this.timeout,
|
||||
}, () => {
|
||||
if (this.state.auto) {
|
||||
this._autoVerifyInterval = setInterval(this._tick.bind(this), 1000);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case firebase.auth.PhoneAuthState.ERROR: // or 'error'
|
||||
// restart the phone flow again on error
|
||||
clearInterval(this._autoVerifyInterval);
|
||||
this.setState({
|
||||
...PhoneAuth.getDefaultState(),
|
||||
error: phoneAuthSnapshot.error.message,
|
||||
});
|
||||
break;
|
||||
|
||||
// ---------------------
|
||||
// ANDROID ONLY EVENTS
|
||||
// ---------------------
|
||||
case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
|
||||
clearInterval(this._autoVerifyInterval);
|
||||
this.setState({
|
||||
sent: true,
|
||||
auto: false,
|
||||
verificationId: phoneAuthSnapshot.verificationId,
|
||||
});
|
||||
break;
|
||||
case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
|
||||
clearInterval(this._autoVerifyInterval);
|
||||
this.setState({
|
||||
sent: true,
|
||||
codeInput: phoneAuthSnapshot.code,
|
||||
verificationId: phoneAuthSnapshot.verificationId,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// will never get here - just for linting
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderInputPhoneNumber() {
|
||||
const { phoneNumber } = this.state;
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text>Enter phone number:</Text>
|
||||
<TextInput
|
||||
autoFocus
|
||||
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
|
||||
onChangeText={value => this.setState({ phoneNumber: value })}
|
||||
placeholder={'Phone number ... '}
|
||||
value={phoneNumber}
|
||||
/>
|
||||
<Button title="Begin Verification" color="green" onPress={this.signIn} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderSendingCode() {
|
||||
const { phoneNumber } = this.state;
|
||||
|
||||
return (
|
||||
<View style={{ paddingBottom: 15 }}>
|
||||
<Text
|
||||
style={{ paddingBottom: 25 }}
|
||||
>
|
||||
{`Sending verification code to '${phoneNumber}'.`}
|
||||
</Text>
|
||||
<ActivityIndicator animating style={{ padding: 50 }} size={'large'} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderAutoVerifyProgress() {
|
||||
const { autoVerifyCountDown, started, error, sent, phoneNumber } = this.state;
|
||||
if (!sent && started && !error.length) {
|
||||
return this.renderSendingCode();
|
||||
}
|
||||
return (
|
||||
<View style={{ padding: 0 }}>
|
||||
<Text
|
||||
style={{ paddingBottom: 25 }}
|
||||
>
|
||||
{`Verification code has been successfully sent to '${phoneNumber}'.`}
|
||||
</Text>
|
||||
<Text
|
||||
style={{ marginBottom: 25 }}
|
||||
>
|
||||
{`We'll now attempt to automatically verify the code for you. This will timeout in ${autoVerifyCountDown} seconds.`}
|
||||
</Text>
|
||||
<Button
|
||||
style={{ paddingTop: 25 }} title="I have a code already" color="green"
|
||||
onPress={() => this.setState({ auto: false })}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderError() {
|
||||
const { error } = this.state;
|
||||
|
||||
return (
|
||||
<View style={{ padding: 10, borderRadius: 5, margin: 10, backgroundColor: 'rgb(255,0,0)' }}>
|
||||
<Text
|
||||
style={{ color: '#fff' }}
|
||||
>
|
||||
{error}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { started, error, codeInput, sent, auto, user } = this.state;
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: user ? 'rgb(0, 200, 0)' : '#fff' }}>
|
||||
<View
|
||||
style={{
|
||||
padding: 5,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Image source={{ uri: imageUrl }} style={{ width: 128, height: 128, marginTop: 25, marginBottom: 15 }} />
|
||||
<Text style={{ fontSize: 25, marginBottom: 20 }}>Phone Auth Example</Text>
|
||||
{error && error.length ? this.renderError() : null}
|
||||
{!started && !sent ? this.renderInputPhoneNumber() : null}
|
||||
{started && auto && !codeInput.length ? this.renderAutoVerifyProgress() : null}
|
||||
{!user && started && sent && (codeInput.length || !auto) ? (
|
||||
<View style={{ marginTop: 15 }}>
|
||||
<Text>Enter verification code below:</Text>
|
||||
<TextInput
|
||||
autoFocus
|
||||
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
|
||||
onChangeText={value => this.setState({ codeInput: value })}
|
||||
placeholder={'Code ... '}
|
||||
value={codeInput}
|
||||
/>
|
||||
<Button title="Confirm Code" color="#841584" onPress={this.afterVerify} />
|
||||
</View>
|
||||
) : null}
|
||||
{user ? (
|
||||
<View style={{ marginTop: 15 }}>
|
||||
<Text>{`Signed in with new user id: '${user.uid}'`}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
{ user ? (
|
||||
<View
|
||||
style={{
|
||||
padding: 15,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#77dd77',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Image source={{ uri: successImageUri }} style={{ width: 100, height: 100, marginBottom: 25 }} />
|
||||
<Text style={{ fontSize: 25 }}>Signed In!</Text>
|
||||
<Text>{JSON.stringify(user)}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
*/
|
||||
|
||||
|
||||
// Example usage if handling here and not in optionalCompleteCb:
|
||||
// const { verificationId, code } = phoneAuthSnapshot;
|
||||
// const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);
|
||||
|
||||
// Do something with your new credential, e.g.:
|
||||
// firebase.auth().signInWithCredential(credential);
|
||||
// firebase.auth().linkWithCredential(credential);
|
||||
// etc ...
|
|
@ -11,6 +11,10 @@ import performance from './perf';
|
|||
import admob from './admob';
|
||||
import firestore from './firestore';
|
||||
|
||||
window.getCoverage = function getCoverage() {
|
||||
return (JSON.stringify(global.__coverage__));
|
||||
};
|
||||
|
||||
const testSuiteInstances = [
|
||||
admob,
|
||||
analytics,
|
||||
|
|
Loading…
Reference in New Issue