2
0
mirror of synced 2025-02-23 03:28:25 +00:00

Merge remote-tracking branch 'upstream/master' into ios-foreground-notif

This commit is contained in:
Kevin 2018-01-10 13:59:50 +08:00
commit 3c0f562716
114 changed files with 5922 additions and 3309 deletions

View File

@ -1,3 +1,16 @@
{
"presets": ["react-native"]
"env": {
"development": {
"presets": ["react-native"],
"plugins": [
"transform-flow-strip-types"
]
},
"publish": {
"presets": ["react-native-syntax"],
"plugins": [
"transform-flow-strip-types"
]
}
}
}

View File

@ -34,7 +34,13 @@
"import/extensions": 0,
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0
"react/jsx-filename-extension": 0,
"no-unused-expressions": 0,
"flowtype/no-unused-expressions": ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}]
},
"globals": {
"__DEV__": true,

View File

@ -1,108 +1,77 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
.*/node_modules/react-native/\.buckd/
# Some modules have their own node_modules with overlap
.*/node_modules/node-haste/.*
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
# React Native problems
.*/node_modules/react-native/Libraries/Animated/src/AnimatedInterpolation.js
.*/node_modules/react-native/Libraries/Animated/src/Interpolation.js
.*/node_modules/react-native/Libraries/BugReporting/dumpReactTree.js
.*/node_modules/react-native/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js
.*/node_modules/react-native/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolater.js
.*/node_modules/react-native/Libraries/Experimental/WindowedListView.js
.*/node_modules/react-native/Libraries/Image/Image.io.js
.*/node_modules/react-native/Libraries/NavigationExperimental/NavigationExperimental.js
.*/node_modules/react-native/Libraries/NavigationExperimental/NavigationHeaderStyleInterpolator.js
.*/node_modules/react-native/Libraries/Network/FormData.js
.*/node_modules/react-native/Libraries/ReactIOS/YellowBox.js
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaCalculator.js.flow
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaPatcher.js.flow
.*/node_modules/metro-bundler/src/node-haste/AssetResolutionCache.js.flow
.*/node_modules/metro-bundler/src/node-haste/DependencyGraph.js.flow
#.*/node_modules/react-native/Libraries/Animated/src/nodes/AnimatedStyle.js
#.*/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableFlatList.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableListView.js
#.*/node_modules/react-native/Libraries/Image/ImageBackground.js
#.*/node_modules/react-native/Libraries/Lists/FlatList.js
#.*/node_modules/react-native/Libraries/Lists/MetroListView.js
#.*/node_modules/react-native/Libraries/Lists/SectionList.js
#.*/node_modules/react-native/Libraries/Lists/ViewabilityHelper.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedList.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedSectionList.js
# Ignore dist folder
.*/dist/.*
# Ignore react and fbjs where there are overlaps, but don't ignore
# anything that react-native relies on
.*/node_modules/fbjs/lib/Map.js
.*/node_modules/fbjs/lib/ErrorUtils.js
# Flow has a built-in definition for the 'react' module which we prefer to use
# over the currently-untyped source
.*/node_modules/react/react.js
.*/node_modules/react/lib/React.js
.*/node_modules/react/lib/ReactDOM.js
.*/__mocks__/.*
.*/__tests__/.*
.*/commoner/test/source/widget/share.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
# See https://github.com/facebook/flow/issues/442
.*/react-tools/node_modules/commoner/lib/reader.js
# Ignore jest
.*/node_modules/jest-cli/.*
# Ignore Website
.*/website/.*
# Ignore generators
.*/local-cli/generator.*
# Ignore BUCK generated folders
.*\.buckd/
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
.*/node_modules/spdx-license-ids/spdx-license-ids.json
.*/node_modules/spdx-exceptions/index.json
.*/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
.*/node_modules/resolve/lib/core.json
.*/node_modules/jsonparse/samplejson/.*\.json
.*/node_modules/json5/test/.*\.json
.*/node_modules/ua-parser-js/test/.*\.json
.*/node_modules/builtin-modules/builtin-modules.json
.*/node_modules/binary-extensions/binary-extensions.json
.*/node_modules/url-regex/tlds.json
.*/node_modules/joi/.*\.json
.*/node_modules/isemail/.*\.json
.*/node_modules/tr46/.*\.json
.*/node_modules/protobufjs/src/bower.json
.*/node_modules/grpc/node_modules/protobufjs/src/bower.json
# Ignore tests project
.*/tests/.*
[include]
node_modules/fbjs/lib
[libs]
lib/flow.js
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
node_modules/fbjs/flow/lib
node_modules/react-native/flow/
[options]
module.system=haste
experimental.strict_type_args=true
unsafe.enable_getters_and_setters=true
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
emoji=true
munge_underscores=true
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
unsafe.enable_getters_and_setters=true
[version]
^0.46.0
^0.56.0

View File

@ -1,14 +1,13 @@
<!---
- BEFORE YOU MAKE AN ISSUE -
-->
BEFORE YOU MAKE AN ISSUE
<!---
1) If you're trying to request a feature then please do so via our request board:
https://react-native-firebase.canny.io/feature-requests
--->
The issue list of this repo is exclusively for bug reports.
<!---
2) If this is a setup issue then please make sure you've correctly followed the setup guides, most setup issues such as 'duplicate dex files', 'default app has not been initialized' etc are all down to an incorrect setup as the guides haven't been correctly followed.
1) For feature requests, please use our Canny board: https://react-native-firebase.canny.io/feature-requests
2) For questions and support please use our Discord chat: https://discord.gg/t6bdqMs or Stack Overflow: https://stackoverflow.com/questions/tagged/react-native-firebase
3) If this is a setup issue then please make sure you've correctly followed the setup guides, most setup issues such as 'duplicate dex files', 'default app has not been initialized' etc are all down to an incorrect setup as the guides haven't been correctly followed.
-->
### Issue

3
.gitignore vendored
View File

@ -76,4 +76,5 @@ local.properties
**/ios/Pods/**
**/ios/ReactNativeFirebaseDemo.xcworkspace/
dist
version.js

View File

@ -11,6 +11,15 @@ npm-debug.log
project.xcworkspace/
xcuserdata/
# Config files
.babelrc
.editorconfig
.eslintrc
.flowconfig
.watchmanconfig
buddybuild_postclone.sh
jsconfig.json
# Example
example/

View File

@ -35,37 +35,38 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
> '**?**' indicates partial support
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | Web SDK |
| ---------------------- | :---: | :---: | :---: | :---: | :---: |
| **AdMob** | ❌ | ✅ | ✅ | ✅ | ❌ |
| **Analytics**             | ✅ | ✅ | ✅ | ✅ | ❌ |
| **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ❌ |
| **Core** | ❌ |**?**| ✅ | ✅ | ✅ |
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ |
| **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ |**?**|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ |**?**|
| **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ |**?**| ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ |**?**|
| _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Remote Config** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Storage** | ✅ | ✅ | ✅ | ✅ |**?**|
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK |
| ---------------------- | :---: | :---: | :---: | :---: | :---: | :---: |
| **AdMob** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Analytics**             | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ |
| **Core** | ❌ |**?**| ✅ | ✅ | ✅ | ✅ |
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ | ✅ |**?**|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| **Crashlytics**           | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ |**?**|**?**| ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Remote Config** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Storage** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
---
### Supported versions - React Native / Firebase
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X |
|------------------------|-------------|-------------|-----------------|----------|----------|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + | 0.48 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + | 11.4.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + | 4.5.0 + |
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X | 3.2.X |
|------------------------|-------------|-------------|-----------------|----------|-------------|----------|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + | 0.48 - 0.49 | 0.50 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + | 11.6.0 + | 11.6.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + | 4.5.0 + | 4.7.0 + |
---

View File

@ -2,9 +2,13 @@ buildscript {
ext.firebaseVersion = '11.4.2'
repositories {
jcenter()
maven {
url 'https://maven.fabric.io/public'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'io.fabric.tools:gradle:1.24.4'
}
}
@ -91,4 +95,7 @@ dependencies {
compile "com.google.firebase:firebase-ads:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$firebaseVersion"
compile('com.crashlytics.sdk.android:crashlytics:2.7.1@aar') {
transitive = true
}
}

View File

@ -142,6 +142,8 @@ class RNFirebaseAdMobUtils {
return AdSize.LEADERBOARD;
case "SMART_BANNER":
return AdSize.SMART_BANNER;
case "SMART_BANNER_LANDSCAPE":
return AdSize.SMART_BANNER;
}
}
}

View File

@ -30,6 +30,7 @@ import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
import com.google.firebase.auth.ActionCodeResult;
import com.google.firebase.auth.ActionCodeSettings;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
@ -321,25 +322,32 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void sendPasswordResetEmail(String appName, final String email, final Promise promise) {
public void sendPasswordResetEmail(String appName, final String email,
ReadableMap actionCodeSettings, final Promise promise) {
Log.d(TAG, "sendPasswordResetEmail");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.sendPasswordResetEmail(email)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendPasswordResetEmail:onComplete:success");
promiseNoUser(promise, false);
} else {
Exception exception = task.getException();
Log.e(TAG, "sendPasswordResetEmail:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendPasswordResetEmail:onComplete:success");
promiseNoUser(promise, false);
} else {
Exception exception = task.getException();
Log.e(TAG, "sendPasswordResetEmail:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
});
}
};
if (actionCodeSettings == null) {
firebaseAuth.sendPasswordResetEmail(email).addOnCompleteListener(listener);
} else {
ActionCodeSettings settings = buildActionCodeSettings(actionCodeSettings);
firebaseAuth.sendPasswordResetEmail(email, settings).addOnCompleteListener(listener);
}
}
/**
@ -439,7 +447,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void sendEmailVerification(String appName, final Promise promise) {
public void sendEmailVerification(String appName, ReadableMap actionCodeSettings, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -450,20 +458,26 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
promiseNoUser(promise, false);
Log.e(TAG, "sendEmailVerification:failure:noCurrentUser");
} else {
user.sendEmailVerification()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendEmailVerification:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
} else {
Exception exception = task.getException();
Log.e(TAG, "sendEmailVerification:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendEmailVerification:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
} else {
Exception exception = task.getException();
Log.e(TAG, "sendEmailVerification:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
});
}
};
if (actionCodeSettings == null) {
user.sendEmailVerification().addOnCompleteListener(listener);
} else {
ActionCodeSettings settings = buildActionCodeSettings(actionCodeSettings);
user.sendEmailVerification(settings).addOnCompleteListener(listener);
}
}
}
@ -1399,6 +1413,30 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
return userMap;
}
private ActionCodeSettings buildActionCodeSettings(ReadableMap actionCodeSettings) {
ActionCodeSettings.Builder builder = ActionCodeSettings.newBuilder();
ReadableMap android = actionCodeSettings.getMap("android");
ReadableMap ios = actionCodeSettings.getMap("iOS");
String url = actionCodeSettings.getString("url");
if (android != null) {
boolean installApp = android.hasKey("installApp") ? android.getBoolean("installApp") : false;
String minimumVersion = android.hasKey("minimumVersion") ? android.getString("minimumVersion") : null;
String packageName = android.getString("packageName");
builder = builder.setAndroidPackageName(packageName, installApp, minimumVersion);
}
if (actionCodeSettings.hasKey("handleCodeInApp")) {
builder = builder.setHandleCodeInApp(actionCodeSettings.getBoolean("handleCodeInApp"));
}
if (ios != null && ios.hasKey("bundleId")) {
builder = builder.setIOSBundleId(ios.getString("bundleId"));
}
if (url != null) {
builder = builder.setUrl(url);
}
return builder.build();
}
/**
* @param appName
* @param requestKey

View File

@ -1,6 +1,7 @@
package io.invertase.firebase.database;
import android.os.AsyncTask;
import android.util.Log;
import android.util.SparseArray;
import com.facebook.react.bridge.Promise;
@ -13,6 +14,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.google.firebase.FirebaseApp;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.Logger;
import com.google.firebase.database.MutableData;
import com.google.firebase.database.OnDisconnect;
@ -35,6 +37,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseDatabase";
private boolean enableLogging = false;
private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
private HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseDatabase(ReactApplicationContext reactContext) {
@ -89,10 +92,19 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
enableLogging = enabled;
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
for (FirebaseApp app : firebaseAppList) {
if (enableLogging) {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.DEBUG);
} else {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.WARN);
loggingLevelSet.put(app.getName(), enabled);
try {
if (enableLogging) {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.DEBUG);
} else {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.WARN);
}
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + app.getName());
}
}
}
@ -487,13 +499,31 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
private FirebaseDatabase getDatabaseForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
Boolean logLevel = loggingLevelSet.get(firebaseDatabase.getApp().getName());
// todo errors 'calls must be made before any other usage of database instance
// if (enableLogging) {
// firebaseDatabase.setLogLevel(Logger.Level.DEBUG);
// } else {
// firebaseDatabase.setLogLevel(Logger.Level.WARN);
// }
if (enableLogging && (logLevel == null || !logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase.getApp().getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.DEBUG);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase.getApp().getName());
}
} else if (!enableLogging && (logLevel != null && logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase.getApp().getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.WARN);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase.getApp().getName());
}
}
return firebaseDatabase;
}

View File

@ -0,0 +1,65 @@
package io.invertase.firebase.fabric.crashlytics;
import android.util.Log;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.crashlytics.android.Crashlytics;
public class RNFirebaseCrashlytics extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseCrashlytics";
public RNFirebaseCrashlytics(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}
@Override
public String getName() {
return TAG;
}
@ReactMethod
public void crash() {
Crashlytics.getInstance().crash();
}
@ReactMethod
public void log(final String message) {
Crashlytics.log(message);
}
@ReactMethod
public void recordError(final int code, final String domain) {
Crashlytics.logException(new Exception(code + ": " + domain));
}
@ReactMethod
public void setBoolValue(final String key, final boolean boolValue) {
Crashlytics.setBool(key, boolValue);
}
@ReactMethod
public void setFloatValue(final String key, final float floatValue) {
Crashlytics.setFloat(key, floatValue);
}
@ReactMethod
public void setIntValue(final String key, final int intValue) {
Crashlytics.setInt(key, intValue);
}
@ReactMethod
public void setStringValue(final String key, final String stringValue) {
Crashlytics.setString(key, stringValue);
}
@ReactMethod
public void setUserIdentifier(String userId) {
Crashlytics.setUserIdentifier(userId);
}
}

View File

@ -0,0 +1,39 @@
package io.invertase.firebase.fabric.crashlytics;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("unused")
public class RNFirebaseCrashlyticsPackage implements ReactPackage {
public RNFirebaseCrashlyticsPackage() {
}
/**
* @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 RNFirebaseCrashlytics(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

@ -10,7 +10,6 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
@ -27,7 +26,6 @@ import java.util.List;
import java.util.Map;
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@ -42,6 +40,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/*
* REACT NATIVE METHODS
*/
/**
* @param enabled
*/
@ReactMethod
public void enableLogging(Boolean enabled) {
FirebaseFirestore.setLoggingEnabled(enabled);
}
@ReactMethod
public void collectionGet(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, final Promise promise) {

View File

@ -104,7 +104,13 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L
@ReactMethod
public void cancelAllLocalNotifications() {
mRNFirebaseLocalMessagingHelper.cancelAllLocalNotifications();
try {
mRNFirebaseLocalMessagingHelper.cancelAllLocalNotifications();
} catch (SecurityException e) {
// In some devices/situations cancelAllLocalNotifications will throw a SecurityException
// We can safely ignore this error for now as the UX impact of this not working is minor.
Log.w(TAG, e.getMessage());
}
}
@ReactMethod

View File

@ -1,5 +0,0 @@
import Firebase from './lib/firebase';
export const AdMob = require('./lib/modules/admob');
export default Firebase;

View File

@ -1,5 +1,5 @@
require 'json'
package = JSON.parse(File.read('package.json'))
package = JSON.parse(File.read('../package.json'))
Pod::Spec.new do |s|
s.name = "RNFirebase"
@ -12,9 +12,8 @@ Pod::Spec.new do |s|
s.license = package['license']
s.authors = "Invertase Limited"
s.source = { :git => "https://github.com/invertase/react-native-firebase.git", :tag => "v#{s.version}" }
s.social_media_url = 'http://twitter.com/mikediarmid'
s.platform = :ios, "8.0"
s.preserve_paths = 'README.md', 'package.json', '*.js'
s.source_files = 'ios/RNFirebase/**/*.{h,m}'
s.social_media_url = 'http://twitter.com/RNFirebase'
s.platform = :ios, "9.0"
s.source_files = 'RNFirebase/**/*.{h,m}'
s.dependency 'React'
end

View File

@ -13,6 +13,7 @@
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
8323CF081F6FBD870071420B /* RNFirebaseAdMobBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */; };
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */; };
833693131FD824EF00AA806B /* RNFirebaseCrashlytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 833693121FD824EF00AA806B /* RNFirebaseCrashlytics.m */; };
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; };
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; };
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; };
@ -57,6 +58,8 @@
8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobBannerManager.m; sourceTree = "<group>"; };
8323CF041F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobNativeExpressManager.h; sourceTree = "<group>"; };
8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobNativeExpressManager.m; sourceTree = "<group>"; };
833693111FD824EF00AA806B /* RNFirebaseCrashlytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseCrashlytics.h; path = RNFirebase/fabric/crashlytics/RNFirebaseCrashlytics.h; sourceTree = SOURCE_ROOT; };
833693121FD824EF00AA806B /* RNFirebaseCrashlytics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseCrashlytics.m; path = RNFirebase/fabric/crashlytics/RNFirebaseCrashlytics.m; sourceTree = SOURCE_ROOT; };
8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreDocumentReference.m; sourceTree = "<group>"; };
8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestore.h; sourceTree = "<group>"; };
8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestore.m; sourceTree = "<group>"; };
@ -124,6 +127,7 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
8336930F1FD80DE800AA806B /* fabric */,
BA84AE541FA9E59800E79390 /* storage */,
17AF4F681F59CDBF00C02336 /* links */,
839D914D1EF3E20A0077C7C8 /* admob */,
@ -144,6 +148,23 @@
);
sourceTree = "<group>";
};
8336930F1FD80DE800AA806B /* fabric */ = {
isa = PBXGroup;
children = (
833693101FD80DF500AA806B /* crashlytics */,
);
path = fabric;
sourceTree = "<group>";
};
833693101FD80DF500AA806B /* crashlytics */ = {
isa = PBXGroup;
children = (
833693111FD824EF00AA806B /* RNFirebaseCrashlytics.h */,
833693121FD824EF00AA806B /* RNFirebaseCrashlytics.m */,
);
path = crashlytics;
sourceTree = "<group>";
};
8376F70D1F7C141500D45A85 /* firestore */ = {
isa = PBXGroup;
children = (
@ -328,6 +349,7 @@
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */,
839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */,
839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */,
833693131FD824EF00AA806B /* RNFirebaseCrashlytics.m in Sources */,
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,
839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */,
BA84AE571FA9E59800E79390 /* RNFirebaseStorage.m in Sources */,
@ -432,6 +454,8 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
@ -450,8 +474,10 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
@ -473,6 +499,8 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
@ -491,8 +519,10 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";

View File

@ -1,4 +1,5 @@
#import "RNFirebase.h"
#import "RNFirebaseUtil.h"
#import <FirebaseCore/FirebaseCore.h>
@implementation RNFirebase
@ -21,14 +22,14 @@ RCT_EXPORT_MODULE(RNFirebase);
* @return
*/
RCT_EXPORT_METHOD(initializeApp:
(NSString *) appName
(NSString *) appDisplayName
options:
(NSDictionary *) options
callback:
(RCTResponseSenderBlock) callback) {
dispatch_sync(dispatch_get_main_queue(), ^{
FIRApp *existingApp = [FIRApp appNamed:appName];
FIRApp *existingApp = [RNFirebaseUtil getApp:appDisplayName];
if (!existingApp) {
FIROptions *firOptions = [[FIROptions alloc] initWithGoogleAppID:[options valueForKey:@"appId"] GCMSenderID:[options valueForKey:@"messagingSenderId"]];
@ -43,6 +44,7 @@ RCT_EXPORT_METHOD(initializeApp:
firOptions.deepLinkURLScheme = [options valueForKey:@"deepLinkURLScheme"];
firOptions.bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
NSString *appName = [RNFirebaseUtil getAppName:appDisplayName];
[FIRApp configureWithName:appName options:firOptions];
}
@ -55,13 +57,13 @@ RCT_EXPORT_METHOD(initializeApp:
* @return
*/
RCT_EXPORT_METHOD(deleteApp:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *existingApp = [FIRApp appNamed:appName];
FIRApp *existingApp = [RNFirebaseUtil getApp:appDisplayName];
if (!existingApp) {
return resolve([NSNull null]);
@ -90,7 +92,7 @@ RCT_EXPORT_METHOD(deleteApp:
NSMutableDictionary *appOptions = [NSMutableDictionary new];
FIRApp *firApp = firApps[key];
FIROptions *firOptions = [firApp options];
appOptions[@"name"] = firApp.name;
appOptions[@"name"] = [RNFirebaseUtil getAppDisplayName:firApp.name];
appOptions[@"apiKey"] = firOptions.APIKey;
appOptions[@"appId"] = firOptions.googleAppID;
appOptions[@"databaseURL"] = firOptions.databaseURL;

View File

@ -3,11 +3,15 @@
#import <Foundation/Foundation.h>
#import <React/RCTEventEmitter.h>
#import <Firebase.h>
@interface RNFirebaseUtil : NSObject
+ (FIRApp *)getApp:(NSString *)appDisplayName;
+ (NSString *)getAppName:(NSString *)appDisplayName;
+ (NSString *)getAppDisplayName:(NSString *)appName;
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body;
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body;
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body;
@end

View File

@ -2,6 +2,28 @@
@implementation RNFirebaseUtil
static NSString *const DEFAULT_APP_DISPLAY_NAME = @"[DEFAULT]";
static NSString *const DEFAULT_APP_NAME = @"__FIRAPP_DEFAULT";
+ (FIRApp *)getApp:(NSString *)appDisplayName {
NSString *appName = [RNFirebaseUtil getAppName:appDisplayName];
return [FIRApp appNamed:appName];
}
+ (NSString *)getAppName:(NSString *)appDisplayName {
if ([appDisplayName isEqualToString:DEFAULT_APP_DISPLAY_NAME]) {
return DEFAULT_APP_NAME;
}
return appDisplayName;
}
+ (NSString *)getAppDisplayName:(NSString *)appName {
if ([appName isEqualToString:DEFAULT_APP_NAME]) {
return DEFAULT_APP_DISPLAY_NAME;
}
return appName;
}
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body {
@try {
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
@ -14,10 +36,10 @@
}
}
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body {
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body {
// Add the appName to the body
NSMutableDictionary *newBody = [body mutableCopy];
newBody[@"appName"] = appName;
newBody[@"appName"] = [RNFirebaseUtil getAppDisplayName:app.name];
[RNFirebaseUtil sendJSEvent:emitter name:name body:newBody];
}

View File

@ -210,6 +210,8 @@ RCT_EXPORT_METHOD(clearInterstitial:
return kGADAdSizeLeaderboard;
} else if ([value isEqualToString:@"SMART_BANNER"]) {
return kGADAdSizeSmartBannerPortrait;
} else if ([value isEqualToString:@"SMART_BANNER_LANDSCAPE"]) {
return kGADAdSizeSmartBannerLandscape;
} else {
return kGADAdSizeBanner;
}

View File

@ -23,19 +23,19 @@ RCT_EXPORT_MODULE();
*/
RCT_EXPORT_METHOD(addAuthStateListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_authStateHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
if (![_authStateHandlers valueForKey:firApp.name]) {
FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
}
}];
_authStateHandlers[appName] = [NSValue valueWithNonretainedObject:newListenerHandle];
_authStateHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
@ -44,12 +44,12 @@ RCT_EXPORT_METHOD(addAuthStateListener:
*/
RCT_EXPORT_METHOD(removeAuthStateListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([_authStateHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:appName]];
[_authStateHandlers removeObjectForKey:appName];
if ([_authStateHandlers valueForKey:firApp.name]) {
[[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:firApp.name]];
[_authStateHandlers removeObjectForKey:firApp.name];
}
}
@ -58,19 +58,19 @@ RCT_EXPORT_METHOD(removeAuthStateListener:
*/
RCT_EXPORT_METHOD(addIdTokenListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_idTokenHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
if (![_idTokenHandlers valueForKey:firApp.name]) {
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
}
}];
_idTokenHandlers[appName] = [NSValue valueWithNonretainedObject:newListenerHandle];
_idTokenHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
@ -79,11 +79,12 @@ RCT_EXPORT_METHOD(addIdTokenListener:
*/
RCT_EXPORT_METHOD(removeIdTokenListener:
(NSString *) appName) {
if ([_idTokenHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:appName]];
[_idTokenHandlers removeObjectForKey:appName];
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([_idTokenHandlers valueForKey:firApp.name]) {
[[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:firApp.name]];
[_idTokenHandlers removeObjectForKey:firApp.name];
}
}
@ -97,12 +98,12 @@ RCT_EXPORT_METHOD(removeIdTokenListener:
@return
*/
RCT_EXPORT_METHOD(signOut:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -125,12 +126,12 @@ RCT_EXPORT_METHOD(signOut:
@return
*/
RCT_EXPORT_METHOD(signInAnonymously:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
if (error) {
@ -152,7 +153,7 @@ RCT_EXPORT_METHOD(signInAnonymously:
@return return
*/
RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
pass:
@ -161,7 +162,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -182,7 +183,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
@return return
*/
RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
pass:
@ -191,7 +192,7 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -210,12 +211,12 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
@return return
*/
RCT_EXPORT_METHOD(delete:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -239,12 +240,12 @@ RCT_EXPORT_METHOD(delete:
@return return
*/
RCT_EXPORT_METHOD(reload:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -262,24 +263,28 @@ RCT_EXPORT_METHOD(reload:
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(sendEmailVerification:
(NSString *) appName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appDisplayName
actionCodeSettings:(NSDictionary *) actionCodeSettings
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
[user sendEmailVerificationWithCompletion:^(NSError *_Nullable error) {
id handler = ^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:userAfterUpdate];
}
}];
};
if (actionCodeSettings) {
FIRActionCodeSettings *settings = [self buildActionCodeSettings:actionCodeSettings];
[user sendEmailVerificationWithActionCodeSettings:settings completion:handler];
} else {
[user sendEmailVerificationWithCompletion:handler];
}
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
@ -294,14 +299,14 @@ RCT_EXPORT_METHOD(sendEmailVerification:
@return return
*/
RCT_EXPORT_METHOD(updateEmail:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -326,14 +331,14 @@ RCT_EXPORT_METHOD(updateEmail:
@return return
*/
RCT_EXPORT_METHOD(updatePassword:
(NSString *) appName
(NSString *) appDisplayName
password:
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -360,14 +365,14 @@ RCT_EXPORT_METHOD(updatePassword:
@return return
*/
RCT_EXPORT_METHOD(updateProfile:
(NSString *) appName
(NSString *) appDisplayName
props:
(NSDictionary *) props
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -408,14 +413,14 @@ RCT_EXPORT_METHOD(updateProfile:
@return
*/
RCT_EXPORT_METHOD(getToken:
(NSString *) appName
(NSString *) appDisplayName
forceRefresh:
(BOOL) forceRefresh
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -443,7 +448,7 @@ RCT_EXPORT_METHOD(getToken:
@return
*/
RCT_EXPORT_METHOD(signInWithCredential:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
token:
@ -454,7 +459,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -481,7 +486,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
@return
*/
RCT_EXPORT_METHOD(confirmPasswordReset:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
newPassword:
@ -490,7 +495,7 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] confirmPasswordResetWithCode:code newPassword:newPassword completion:^(NSError *_Nullable error) {
if (error) {
@ -511,14 +516,14 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
* @return
*/
RCT_EXPORT_METHOD(applyActionCode:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] applyActionCode:code completion:^(NSError *_Nullable error) {
if (error) {
@ -538,14 +543,14 @@ RCT_EXPORT_METHOD(applyActionCode:
* @return
*/
RCT_EXPORT_METHOD(checkActionCode:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) {
if (error) {
@ -582,23 +587,27 @@ RCT_EXPORT_METHOD(checkActionCode:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(sendPasswordResetEmail:
(NSString *) appName
email:
(NSString *) email
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
email:(NSString *) email
actionCodeSettings:(NSDictionary *) actionCodeSettings
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] sendPasswordResetWithEmail:email completion:^(NSError *_Nullable error) {
id handler = ^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseNoUser:resolve rejecter:reject isError:NO];
}
}];
};
if (actionCodeSettings) {
FIRActionCodeSettings *settings = [self buildActionCodeSettings:actionCodeSettings];
[[FIRAuth authWithApp:firApp] sendPasswordResetWithEmail:email actionCodeSettings:settings completion:handler];
} else {
[[FIRAuth authWithApp:firApp] sendPasswordResetWithEmail:email completion:handler];
}
}
/**
@ -609,12 +618,12 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:
@return
*/
RCT_EXPORT_METHOD(getCurrentUser:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:user];
@ -629,14 +638,14 @@ RCT_EXPORT_METHOD(getCurrentUser:
@return
*/
RCT_EXPORT_METHOD(signInWithCustomToken:
(NSString *) appName
(NSString *) appDisplayName
customToken:
(NSString *) customToken
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -655,11 +664,11 @@ RCT_EXPORT_METHOD(signInWithCustomToken:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
@ -682,10 +691,10 @@ RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber
requestKey:(NSString *) requestKey) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
@ -695,7 +704,7 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey,
@"state": @{@"error": jsError},
};
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
} else {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"];
@ -704,16 +713,16 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey,
@"state": @{@"verificationId": verificationID},
};
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
}
}];
}
RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
verificationCode:(NSString *) verificationCode
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *verificationId = [defaults stringForKey:@"authVerificationID"];
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode];
@ -738,7 +747,7 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
@return
*/
RCT_EXPORT_METHOD(link:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
authToken:
@ -749,7 +758,7 @@ RCT_EXPORT_METHOD(link:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -783,14 +792,14 @@ RCT_EXPORT_METHOD(link:
@return
*/
RCT_EXPORT_METHOD(unlink:
(NSString *) appName
(NSString *) appDisplayName
providerId:
(NSString *) providerId
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -817,7 +826,7 @@ RCT_EXPORT_METHOD(unlink:
@return
*/
RCT_EXPORT_METHOD(reauthenticate:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
authToken:
@ -828,7 +837,7 @@ RCT_EXPORT_METHOD(reauthenticate:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -861,14 +870,14 @@ RCT_EXPORT_METHOD(reauthenticate:
@return
*/
RCT_EXPORT_METHOD(fetchProvidersForEmail:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] fetchProvidersForEmail:email completion:^(NSArray<NSString *> *_Nullable providers, NSError *_Nullable error) {
if (error) {
@ -1154,6 +1163,30 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
return userDict;
}
- (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSettings {
FIRActionCodeSettings *settings = [[FIRActionCodeSettings alloc] init];
NSDictionary *android = actionCodeSettings[@"android"];
BOOL handleCodeInApp = actionCodeSettings[@"handleCodeInApp"];
NSDictionary *ios = actionCodeSettings[@"iOS"];
NSString *url = actionCodeSettings[@"url"];
if (android) {
BOOL installApp = android[@"installApp"];
NSString *minimumVersion = android[@"minimumVersion"];
NSString *packageName = android[@"packageName"];
[settings setAndroidPackageName:packageName installIfNotAvailable:installApp minimumVersion:minimumVersion];
}
if (handleCodeInApp) {
[settings setHandleCodeInApp:handleCodeInApp];
}
if (ios && ios[@"bundleId"]) {
[settings setIOSBundleID:ios[@"bundleId"]];
}
if (url) {
[settings setURL:[NSURL URLWithString:url]];
}
return settings;
}
- (NSArray<NSString *> *)supportedEvents {
return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT];
}

View File

@ -16,7 +16,7 @@
+ (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject databaseError:(NSError *)databaseError;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appName;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError;

View File

@ -22,38 +22,38 @@ RCT_EXPORT_MODULE();
return self;
}
RCT_EXPORT_METHOD(goOnline:(NSString *) appName) {
[[RNFirebaseDatabase getDatabaseForApp:appName] goOnline];
RCT_EXPORT_METHOD(goOnline:(NSString *) appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOnline];
}
RCT_EXPORT_METHOD(goOffline:(NSString *) appName) {
[[RNFirebaseDatabase getDatabaseForApp:appName] goOffline];
RCT_EXPORT_METHOD(goOffline:(NSString *) appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOffline];
}
RCT_EXPORT_METHOD(setPersistence:(NSString *) appName
RCT_EXPORT_METHOD(setPersistence:(NSString *) appDisplayName
state:(BOOL) state) {
[RNFirebaseDatabase getDatabaseForApp:appName].persistenceEnabled = state;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceEnabled = state;
}
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *) appName
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *) appDisplayName
size:(NSInteger *) size) {
[RNFirebaseDatabase getDatabaseForApp:appName].persistenceCacheSizeBytes = (NSUInteger)size;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceCacheSizeBytes = (NSUInteger)size;
}
RCT_EXPORT_METHOD(enableLogging:(BOOL) enabled) {
[FIRDatabase setLoggingEnabled:enabled];
}
RCT_EXPORT_METHOD(keepSynced:(NSString *) appName
RCT_EXPORT_METHOD(keepSynced:(NSString *) appDisplayName
key:(NSString *) key
path:(NSString *) path
modifiers:(NSArray *) modifiers
state:(BOOL) state) {
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers].query;
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers].query;
[query keepSynced:state];
}
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appName
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appDisplayName
transactionId:(nonnull NSNumber *) transactionId
updates:(NSDictionary *) updates) {
__block NSMutableDictionary *transactionState;
@ -82,7 +82,7 @@ RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appName
}
RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
RCT_EXPORT_METHOD(transactionStart:(NSString *) appDisplayName
path:(NSString *) path
transactionId:(nonnull NSNumber *) transactionId
applyLocally:(BOOL) applyLocally) {
@ -90,13 +90,13 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
NSMutableDictionary *transactionState = [NSMutableDictionary new];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
transactionState[@"semaphore"] = sema;
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData *
_Nonnull currentData) {
dispatch_barrier_async(_transactionQueue, ^{
[_transactions setValue:transactionState forKey:transactionId];
NSDictionary *updateMap = [self createTransactionUpdateMap:appName transactionId:transactionId updatesData:currentData];
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName transactionId:transactionId updatesData:currentData];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:updateMap];
});
@ -123,7 +123,7 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
}
andCompletionBlock:
^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) {
NSDictionary *resultMap = [self createTransactionResultMap:appName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:resultMap];
}
withLocalEvents:
@ -131,117 +131,117 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
});
}
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectUpdateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(set:(NSString *) appName
RCT_EXPORT_METHOD(set:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setValue:[props valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setPriority:(NSString *) appName
RCT_EXPORT_METHOD(setPriority:(NSString *) appDisplayName
path:(NSString *) path
priority:(NSDictionary *) priority
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setWithPriority:(NSString *) appName
RCT_EXPORT_METHOD(setWithPriority:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
priority:(NSDictionary *) priority
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setValue:[data valueForKey:@"value"] andPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(update:(NSString *) appName
RCT_EXPORT_METHOD(update:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref updateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(remove:(NSString *) appName
RCT_EXPORT_METHOD(remove:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(once:(NSString *) appName
RCT_EXPORT_METHOD(once:(NSString *) appDisplayName
key:(NSString *) key
path:(NSString *) path
modifiers:(NSArray *) modifiers
eventName:(NSString *) eventName
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers];
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers];
[ref once:eventName resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(on:(NSString *) appName
RCT_EXPORT_METHOD(on:(NSString *) appDisplayName
props:(NSDictionary *) props) {
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appName props:props];
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName props:props];
[ref on:props[@"eventType"] registration:props[@"registration"]];
}
@ -271,20 +271,20 @@ RCT_EXPORT_METHOD(off:(NSString *) key
}
}
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appName {
FIRApp *app = [FIRApp appNamed:appName];
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName {
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
return [FIRDatabase databaseForApp:app];
}
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appName path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appName] referenceWithPath:path];
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName] referenceWithPath:path];
}
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName key:key refPath:path modifiers:modifiers];
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
}
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appName props:(NSDictionary *)props {
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName props:(NSDictionary *)props {
NSString *key = props[@"key"];
NSString *path = props[@"path"];
NSDictionary *modifiers = props[@"modifiers"];
@ -292,7 +292,7 @@ RCT_EXPORT_METHOD(off:(NSString *) key
RNFirebaseDatabaseReference *ref = _dbReferences[key];
if (ref == nil) {
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName key:key refPath:path modifiers:modifiers];
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
_dbReferences[key] = ref;
}
return ref;
@ -380,20 +380,20 @@ RCT_EXPORT_METHOD(off:(NSString *) key
return errorMap;
}
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
NSMutableDictionary *updatesMap = [[NSMutableDictionary alloc] init];
[updatesMap setValue:transactionId forKey:@"id"];
[updatesMap setValue:@"update" forKey:@"type"];
[updatesMap setValue:appName forKey:@"appName"];
[updatesMap setValue:appDisplayName forKey:@"appName"];
[updatesMap setValue:updatesData.value forKey:@"value"];
return updatesMap;
}
- (NSDictionary *)createTransactionResultMap:(NSString *)appName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
NSMutableDictionary *resultMap = [[NSMutableDictionary alloc] init];
[resultMap setValue:transactionId forKey:@"id"];
[resultMap setValue:appName forKey:@"appName"];
[resultMap setValue:appDisplayName forKey:@"appName"];
// TODO: no timeout on iOS
[resultMap setValue:@(committed) forKey:@"committed"];
// TODO: no interrupted on iOS

View File

@ -12,12 +12,12 @@
@interface RNFirebaseDatabaseReference : NSObject
@property RCTEventEmitter *emitter;
@property FIRDatabaseQuery *query;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *key;
@property NSString *path;
@property NSMutableDictionary *listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (void)on:(NSString *) eventName registration:(NSDictionary *) registration;
- (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)removeEventListener:(NSString *)eventRegistrationKey;

View File

@ -5,14 +5,14 @@
#if __has_include(<FirebaseDatabase/FIRDatabase.h>)
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
key:(NSString *) key
refPath:(NSString *) refPath
modifiers:(NSArray *) modifiers {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_key = key;
_path = refPath;
_listeners = [[NSMutableDictionary alloc] init];
@ -123,7 +123,7 @@
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *) path
modifiers:(NSArray *)modifiers {
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_app];
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName];
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
for (NSDictionary *modifier in modifiers) {

View File

@ -0,0 +1,19 @@
#ifndef RNFirebaseCrashlytics_h
#define RNFirebaseCrashlytics_h
#import <Foundation/Foundation.h>
#if __has_include(<Crashlytics/Crashlytics.h>)
#import <React/RCTBridgeModule.h>
@interface RNFirebaseCrashlytics : NSObject <RCTBridgeModule> {
}
@end
#else
@interface RNFirebaseCrashlytics : NSObject
@end
#endif
#endif

View File

@ -0,0 +1,47 @@
#import "RNFirebaseCrashlytics.h"
#if __has_include(<Crashlytics/Crashlytics.h>)
#import <Crashlytics/Crashlytics.h>
@implementation RNFirebaseCrashlytics
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(crash) {
[[Crashlytics sharedInstance] crash];
}
RCT_EXPORT_METHOD(log:(NSString *)message) {
CLS_LOG(@"%@", message);
}
RCT_EXPORT_METHOD(recordError:(nonnull NSNumber *)code domain:(NSString *)domain) {
NSError *error = [NSError errorWithDomain:domain code:[code integerValue] userInfo:nil];
[CrashlyticsKit recordError:error];
}
RCT_EXPORT_METHOD(setBoolValue:(NSString *)key boolValue:(BOOL *)boolValue) {
[CrashlyticsKit setBoolValue:boolValue forKey:key];
}
RCT_EXPORT_METHOD(setFloatValue:(NSString *)key floatValue:(nonnull NSNumber *)floatValue) {
[CrashlyticsKit setFloatValue:[floatValue floatValue] forKey:key];
}
RCT_EXPORT_METHOD(setIntValue:(NSString *)key intValue:(nonnull NSNumber *)intValue) {
[CrashlyticsKit setIntValue:[intValue integerValue] forKey:key];
}
RCT_EXPORT_METHOD(setStringValue:(NSString *)key stringValue:(NSString *)stringValue) {
[CrashlyticsKit setObjectValue:stringValue forKey:key];
}
RCT_EXPORT_METHOD(setUserIdentifier:(NSString *)userId) {
[CrashlyticsKit setUserIdentifier:userId];
}
@end
#else
@implementation RNFirebaseCrashlytics
@end
#endif

View File

@ -13,7 +13,7 @@
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError;
@end

View File

@ -18,17 +18,21 @@ RCT_EXPORT_MODULE();
return self;
}
RCT_EXPORT_METHOD(collectionGet:(NSString *) appName
RCT_EXPORT_METHOD(enableLogging:(BOOL) enabled) {
[FIRFirestore enableLogging:enabled];
}
RCT_EXPORT_METHOD(collectionGet:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
[[self getCollectionForAppPath:appDisplayName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
@ -37,22 +41,22 @@ RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
[RNFirebaseFirestoreCollectionReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
listenerId:(nonnull NSString *) listenerId
queryListenOptions:(NSDictionary *) queryListenOptions) {
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appDisplayName path:path filters:filters orders:orders options:options];
[ref onSnapshot:listenerId queryListenOptions:queryListenOptions];
}
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
RCT_EXPORT_METHOD(documentBatch:(NSString *) appDisplayName
writes:(NSArray *) writes
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName];
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appDisplayName];
FIRWriteBatch *batch = [firestore batch];
for (NSDictionary *write in writes) {
@ -85,56 +89,56 @@ RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
}];
}
RCT_EXPORT_METHOD(documentDelete:(NSString *) appName
RCT_EXPORT_METHOD(documentDelete:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] delete:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] delete:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGet:(NSString *) appName
RCT_EXPORT_METHOD(documentGet:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appDisplayName
documents:(NSString *) documents
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
// Not supported on iOS out of the box
}
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appDisplayName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId) {
[RNFirebaseFirestoreDocumentReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appDisplayName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId
docListenOptions:(NSDictionary *) docListenOptions) {
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appDisplayName path:path];
[ref onSnapshot:listenerId docListenOptions:docListenOptions];
}
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
RCT_EXPORT_METHOD(documentSet:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] set:data options:options resolver:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] set:data options:options resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] update:data resolver:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] update:data resolver:resolve rejecter:reject];
}
/*
@ -145,17 +149,17 @@ RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
}
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName {
FIRApp *app = [FIRApp appNamed:appName];
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName {
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
return [FIRFirestore firestoreForApp:app];
}
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self app:appName path:path filters:filters orders:orders options:options];
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appDisplayName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName path:path filters:filters orders:orders options:options];
}
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path {
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self app:appName path:path];
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appDisplayName path:(NSString *)path {
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self appDisplayName:appDisplayName path:path];
}
// TODO: Move to error util for use in other modules

View File

@ -13,14 +13,14 @@
@interface RNFirebaseFirestoreCollectionReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *path;
@property NSArray *filters;
@property NSArray *orders;
@property NSDictionary *options;
@property FIRQuery *query;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;
- (void)onSnapshot:(NSString *)listenerId queryListenOptions:(NSDictionary *) queryListenOptions;

View File

@ -7,7 +7,7 @@
static NSMutableDictionary *_listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
@ -15,7 +15,7 @@ static NSMutableDictionary *_listeners;
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_path = path;
_filters = filters;
_orders = orders;
@ -81,7 +81,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
}
- (FIRQuery *)buildQuery {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_app];
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
FIRQuery *query = (FIRQuery*)[firestore collectionWithPath:_path];
query = [self applyFilters:firestore query:query];
query = [self applyOrders:query];
@ -152,7 +152,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (void)handleQuerySnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
@ -163,7 +163,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];

View File

@ -13,11 +13,11 @@
@interface RNFirebaseFirestoreDocumentReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *path;
@property FIRDocumentReference *ref;
- (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path;
- (id)initWithPath:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName path:(NSString *)path;
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;

View File

@ -7,14 +7,14 @@
static NSMutableDictionary *_listeners;
- (id)initWithPath:(RCTEventEmitter *)emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
path:(NSString *) path {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_path = path;
_ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path];
_ref = [[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] documentWithPath:_path];
}
// Initialise the static listeners object if required
if (!_listeners) {
@ -78,7 +78,7 @@ static NSMutableDictionary *_listeners;
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_app] jsMap:data];
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
if (options && options[@"merge"]) {
[_ref setData:dictionary options:[FIRSetOptions merge] completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
@ -93,7 +93,7 @@ static NSMutableDictionary *_listeners;
- (void)update:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_app] jsMap:data];
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
[_ref updateData:dictionary completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
@ -131,7 +131,7 @@ static NSMutableDictionary *_listeners;
- (void)handleDocumentSnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
@ -142,7 +142,7 @@ static NSMutableDictionary *_listeners;
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];

View File

@ -268,6 +268,11 @@ RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RC
}
}
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
@end
#else

View File

@ -23,11 +23,11 @@ RCT_EXPORT_MODULE(RNFirebaseStorage);
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
@param NSString path
*/
RCT_EXPORT_METHOD(delete:(NSString *) appName
RCT_EXPORT_METHOD(delete:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef deleteWithCompletion:^(NSError *_Nullable error) {
if (error != nil) {
@ -44,11 +44,11 @@ RCT_EXPORT_METHOD(delete:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
@param NSString path
*/
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef downloadURLWithCompletion:^(NSURL *_Nullable URL, NSError *_Nullable error) {
if (error != nil) {
@ -65,11 +65,11 @@ RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
@param NSString path
*/
RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
RCT_EXPORT_METHOD(getMetadata:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef metadataWithCompletion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
if (error != nil) {
@ -87,12 +87,12 @@ RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
@param NSString path
@param NSDictionary metadata
*/
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appDisplayName
path:(NSString *) path
metadata:(NSDictionary *) metadata
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
[fileRef updateMetadata:firmetadata completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
@ -111,12 +111,12 @@ RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName
@param NSString path
@param NSString localPath
*/
RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
RCT_EXPORT_METHOD(downloadFile:(NSString *) appDisplayName
path:(NSString *) path
localPath:(NSString *) localPath
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
NSURL *localFile = [NSURL fileURLWithPath:localPath];
FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile];
@ -124,25 +124,25 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
[downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// download resumed, also fires when the upload starts
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// download paused
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// download reported progress
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// download completed successfully
NSDictionary *resp = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
resolve(resp);
}];
@ -161,9 +161,10 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxDownloadRetryTime:[milliseconds doubleValue]];
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appDisplayName
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxDownloadRetryTime:[milliseconds doubleValue]];
}
/**
@ -172,9 +173,10 @@ RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxOperationRetryTime:[milliseconds doubleValue]];
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appDisplayName
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxOperationRetryTime:[milliseconds doubleValue]];
}
/**
@ -182,9 +184,10 @@ RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
*/
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxUploadRetryTime:[milliseconds doubleValue]];
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appDisplayName
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxUploadRetryTime:[milliseconds doubleValue]];
}
/**
@ -195,7 +198,7 @@ RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName
@param NSString localPath
@param NSDictionary metadata
*/
RCT_EXPORT_METHOD(putFile:(NSString *) appName
RCT_EXPORT_METHOD(putFile:(NSString *) appDisplayName
path:(NSString *) path
localPath:(NSString *) localPath
metadata:(NSDictionary *) metadata
@ -224,7 +227,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
if (info[PHImageErrorKey] == nil) {
if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) {
firmetadata.contentType = [self utiToMimeType:dataUTI];
[self uploadData:appName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadData:appDisplayName 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);
@ -236,7 +239,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
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];
[self uploadData:appDisplayName 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);
@ -260,7 +263,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
firmetadata.contentType = [self utiToMimeType:exportSession.outputFileType];
[self uploadFile:appName url:tempUrl firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadFile:appDisplayName url:tempUrl firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
// we're not cleaning up the temporary file at the moment, just relying on the OS to do that in it's own time - todo?
} else {
reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil);
@ -274,7 +277,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
} else {
// TODO: Content type for file?
NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath];
[self uploadData:appName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadData:appDisplayName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
}
}
@ -288,42 +291,42 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
return (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)dataUTI, kUTTagClassMIMEType);
}
- (void)uploadFile:(NSString *)appName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
- (void)uploadFile:(NSString *)appDisplayName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putFile:url metadata:firmetadata];
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
[self addUploadObservers:appDisplayName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
}
- (void)uploadData:(NSString *)appName data:(NSData *)data firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
- (void)uploadData:(NSString *)appDisplayName data:(NSData *)data firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putData:data metadata:firmetadata];
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
[self addUploadObservers:appDisplayName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
}
- (void)addUploadObservers:(NSString *)appName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
- (void)addUploadObservers:(NSString *)appDisplayName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
// listen for state changes, errors, and completion of the upload.
[uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload resumed, also fires when the upload starts
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload paused
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload reported progress
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload completed successfully
NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
resolve(resp);
}];
@ -335,12 +338,13 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
}
- (FIRStorageReference *)getReference:(NSString *)path
appName:(NSString *)appName {
appDisplayName:(NSString *)appDisplayName {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([path hasPrefix:@"url::"]) {
NSString *url = [path substringFromIndex:5];
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceForURL:url];
return [[FIRStorage storageForApp:firApp] referenceForURL:url];
} else {
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceWithPath:path];
return [[FIRStorage storageForApp:firApp] referenceWithPath:path];
}
}
@ -387,13 +391,13 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
return @[STORAGE_EVENT, STORAGE_ERROR];
}
- (void)sendJSError:(NSString *)appName error:(NSError *)error path:(NSString *)path {
- (void)sendJSError:(NSString *)appDisplayName error:(NSError *)error path:(NSString *)path {
NSDictionary *evt = @{@"path": path, @"message": [error debugDescription]};
[self sendJSEvent:appName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
[self sendJSEvent:appDisplayName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
}
- (void)sendJSEvent:(NSString *)appName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
[RNFirebaseUtil sendJSEvent:self name:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
- (void)sendJSEvent:(NSString *)appDisplayName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
[RNFirebaseUtil sendJSEvent:self name:type body:@{@"eventName": title, @"appName": appDisplayName, @"path": path, @"body": props}];
}
/**

View File

@ -1,175 +0,0 @@
import { NativeModules } from 'react-native';
import INTERNALS from './internals';
import { isObject, isAndroid } from './utils';
import AdMob, { statics as AdMobStatics } from './modules/admob';
import Auth, { statics as AuthStatics } from './modules/auth';
import Analytics from './modules/analytics';
import Crash from './modules/crash';
import Performance from './modules/perf';
import RemoteConfig from './modules/config';
import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
import Messaging, { statics as MessagingStatics } from './modules/messaging';
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
import Links, { statics as LinksStatics } from './modules/links';
import Utils, { statics as UtilsStatics } from './modules/utils';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class FirebaseApp {
constructor(name: string, options: Object = {}) {
this._name = name;
this._namespaces = {};
this._options = Object.assign({}, options);
// native ios/android to confirm initialized
this._initialized = false;
this._nativeInitialized = false;
// modules
this.admob = this._staticsOrModuleInstance(AdMobStatics, AdMob);
this.auth = this._staticsOrModuleInstance(AuthStatics, Auth);
this.analytics = this._staticsOrModuleInstance({}, Analytics);
this.config = this._staticsOrModuleInstance({}, RemoteConfig);
this.crash = this._staticsOrModuleInstance({}, Crash);
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
this.perf = this._staticsOrModuleInstance({}, Performance);
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
this.utils = this._staticsOrModuleInstance(UtilsStatics, Utils);
this._extendedProps = {};
}
/**
*
* @param native
* @private
*/
_initializeApp(native = false) {
if (native) {
// for apps already initialized natively that
// we have info from RN constants
this._initialized = true;
this._nativeInitialized = true;
} else {
FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => {
this._initialized = true;
INTERNALS.SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
});
}
}
/**
*
* @return {*}
*/
get name() {
if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME) {
// ios and android firebase sdk's return different
// app names - so we just return what the web sdk
// would if it was default.
return '[DEFAULT]';
}
return this._name;
}
/**
*
* @return {*}
*/
get options() {
return Object.assign({}, this._options);
}
/**
* Undocumented firebase web sdk method that allows adding additional properties onto
* a firebase app instance.
*
* See: https://github.com/firebase/firebase-js-sdk/blob/master/tests/app/firebase_app.test.ts#L328
*
* @param props
*/
extendApp(props: Object) {
if (!isObject(props)) throw new Error(INTERNALS.ERROR_MISSING_ARG('Object', 'extendApp'));
const keys = Object.keys(props);
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) {
throw new Error(INTERNALS.ERROR_PROTECTED_PROP(key));
}
this[key] = props[key];
this._extendedProps[key] = true;
}
}
/**
*
* @return {Promise}
*/
delete() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete'));
// TODO only the ios sdk currently supports delete, add back in when android also supports it
// if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME && this._nativeInitialized) {
// return Promise.reject(
// new Error('Unable to delete the default native firebase app instance.'),
// );
// }
//
// return FirebaseCoreModule.deleteApp(this._name);
}
/**
*
* @return {*}
*/
onReady(): Promise {
if (this._initialized) return Promise.resolve(this);
return new Promise((resolve, reject) => {
INTERNALS.SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
if (error) return reject(new Error(error)); // error is a string as it's from native
return resolve(this); // return app
});
});
}
/**
*
* @param name
* @param statics
* @param InstanceClass
* @return {function()}
* @private
*/
_staticsOrModuleInstance(statics = {}, InstanceClass): Function {
const getInstance = () => {
const _name = `_${InstanceClass._NAMESPACE}`;
if (isAndroid && InstanceClass._NAMESPACE !== Utils._NAMESPACE && !INTERNALS.FLAGS.checkedPlayServices) {
INTERNALS.FLAGS.checkedPlayServices = true;
this.utils().checkPlayServicesAvailability();
}
if (!this._namespaces[_name]) {
this._namespaces[_name] = new InstanceClass(this, this._options);
}
return this._namespaces[_name];
};
Object.assign(getInstance, statics, {
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
});
return getInstance;
}
}

View File

@ -1,228 +0,0 @@
/**
* @providesModule Firebase
* @flow
*/
import { NativeModules, NativeEventEmitter } from 'react-native';
import INTERNALS from './internals';
import FirebaseApp from './firebase-app';
import { isObject, isString, isAndroid } from './utils';
// module imports
import AdMob, { statics as AdMobStatics } from './modules/admob';
import Auth, { statics as AuthStatics } from './modules/auth';
import Analytics from './modules/analytics';
import Crash from './modules/crash';
import Performance from './modules/perf';
import Links, { statics as LinksStatics } from './modules/links';
import RemoteConfig from './modules/config';
import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
import Messaging, { statics as MessagingStatics } from './modules/messaging';
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
import Utils, { statics as UtilsStatics } from './modules/utils';
const FirebaseCoreModule = NativeModules.RNFirebase;
class FirebaseCore {
constructor() {
this._nativeEmitters = {};
this._nativeSubscriptions = {};
if (!FirebaseCoreModule) {
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
}
this._initializeNativeApps();
// modules
this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob);
this.auth = this._appNamespaceOrStatics(AuthStatics, Auth);
this.analytics = this._appNamespaceOrStatics({}, Analytics);
this.config = this._appNamespaceOrStatics({}, RemoteConfig);
this.crash = this._appNamespaceOrStatics({}, Crash);
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
this.utils = this._appNamespaceOrStatics(UtilsStatics, Utils);
}
/**
* Bootstraps all native app instances that were discovered on boot
* @private
*/
_initializeNativeApps() {
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
const app = FirebaseCoreModule.apps[i];
const options = Object.assign({}, app);
delete options.name;
INTERNALS.APPS[app.name] = new FirebaseApp(app.name, options);
INTERNALS.APPS[app.name]._initializeApp(true);
}
}
/**
* Web SDK initializeApp
*
* @param options
* @param name
* @return {*}
*/
initializeApp(options: Object = {}, name: string): FirebaseApp {
if (name && !isString(name)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
}
const _name = (name || INTERNALS.STRINGS.DEFAULT_APP_NAME).toUpperCase();
// return an existing app if found
// todo in v4 remove deprecation and throw an error
if (INTERNALS.APPS[_name]) {
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
return INTERNALS.APPS[_name];
}
// only validate if app doesn't already exist
// to allow apps already initialized natively
// to still go through init without erroring (backwards compatibility)
if (!isObject(options)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_OBJECT);
}
if (!options.apiKey) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('apiKey'));
}
if (!options.appId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('appId'));
}
if (!options.databaseURL) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('databaseURL'));
}
if (!options.messagingSenderId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('messagingSenderId'));
}
if (!options.projectId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('projectId'));
}
if (!options.storageBucket) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('storageBucket'));
}
INTERNALS.APPS[_name] = new FirebaseApp(_name, options);
// only initialize if certain props are available
if (options.databaseURL && options.apiKey) {
INTERNALS.APPS[_name]._initializeApp();
}
return INTERNALS.APPS[_name];
}
/**
* Retrieves a Firebase app instance.
*
* When called with no arguments, the default app is returned.
* When an app name is provided, the app corresponding to that name is returned.
*
* @param name
* @return {*}
*/
app(name?: string): FirebaseApp {
const _name = name ? name.toUpperCase() : INTERNALS.STRINGS.DEFAULT_APP_NAME;
const app = INTERNALS.APPS[_name];
if (!app) throw new Error(INTERNALS.STRINGS.ERROR_APP_NOT_INIT(_name));
return app;
}
/**
* A (read-only) array of all initialized apps.
* @return {Array}
*/
get apps(): Array<Object> {
return Object.values(INTERNALS.APPS);
}
/*
* INTERNALS
*/
/**
* Subscribe to a native event for js side distribution by appName
* React Native events are hard set at compile - cant do dynamic event names
* so we use a single event send it to js and js then internally can prefix it
* and distribute dynamically.
*
* @param eventName
* @param nativeEmitter
* @private
*/
_subscribeForDistribution(eventName, nativeEmitter) {
if (!this._nativeSubscriptions[eventName]) {
nativeEmitter.addListener(eventName, (event) => {
if (event.appName) {
// native event has an appName property - auto prefix and internally emit
INTERNALS.SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
} else {
// standard event - no need to prefix
INTERNALS.SharedEventEmitter.emit(eventName, event);
}
});
this._nativeSubscriptions[eventName] = true;
}
}
/**
*
* @param statics
* @param InstanceClass
* @return {function(FirebaseApp=)}
* @private
*/
_appNamespaceOrStatics(statics = {}, InstanceClass): Function {
const namespace = InstanceClass._NAMESPACE;
const getNamespace = (app?: FirebaseApp) => {
let _app = app;
// throw an error if it's not a valid app instance
if (_app && !(_app instanceof FirebaseApp)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
// default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
else if (!_app) _app = this.app(INTERNALS.STRINGS.DEFAULT_APP_NAME);
return INTERNALS.APPS[_app._name][namespace](_app);
};
Object.assign(getNamespace, statics, {
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
});
return getNamespace;
}
/**
*
* @param name
* @param nativeModule
* @return {*}
* @private
*/
_getOrSetNativeEmitter(name, nativeModule) {
if (this._nativeEmitters[name]) {
return this._nativeEmitters[name];
}
return this._nativeEmitters[name] = new NativeEventEmitter(nativeModule);
}
}
export default new FirebaseCore();

View File

@ -1,50 +0,0 @@
/* eslint-disable */
// declare module 'react-native' {
// // noinspection ES6ConvertVarToLetConst
// declare var exports: any;
// }
declare type AuthResultType = {
authenticated: boolean,
user: Object|null
} | null;
declare type CredentialType = {
providerId: string,
token: string,
secret: string
};
declare type DatabaseListener = {
listenerId: number;
eventName: string;
successCallback: Function;
failureCallback?: Function;
};
declare type DatabaseModifier = {
type: 'orderBy' | 'limit' | 'filter';
name?: string;
key?: string;
limit?: number;
value?: any;
valueType?: string;
};
declare type GoogleApiAvailabilityType = {
status: number,
isAvailable: boolean,
isUserResolvableError?: boolean,
hasResolution?: boolean,
error?: string
};
declare class FirebaseError {
message: string,
name: string,
code: string,
stack: string,
path: string,
details: string,
modifiers: string
};

View File

@ -64,6 +64,10 @@ declare module "react-native-firebase" {
*/
crash(): RNFirebase.crash.Crash;
static fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics;
};
apps: Array<string>;
googleApiAvailability: RNFirebase.GoogleApiAvailabilityType;
@ -846,5 +850,50 @@ declare module "react-native-firebase" {
[key: string]: any;
}
}
namespace crashlytics {
interface Crashlytics {
/**
* Forces a crash. Useful for testing your application is set up correctly.
*/
crash(): void;
/**
* Logs a message that will appear in any subsequent crash reports.
*/
log(message: string): void;
/**
* Logs a non fatal exception.
*/
recordError(code: number, message: string): void;
/**
* Set a boolean value to show alongside any subsequent crash reports.
*/
setBoolValue(key: string, value: boolean): void;
/**
* Set a float value to show alongside any subsequent crash reports.
*/
setFloatValue(key: string, value: number): void;
/**
* Set an integer value to show alongside any subsequent crash reports.
*/
setIntValue(key: string, value: number): void;
/**
* Set a string value to show alongside any subsequent crash reports.
*/
setStringValue(key: string, value: string): void;
/**
* Set the user ID to show alongside any subsequent crash reports.
*/
setUserIdentifier(userId: string): void;
}
}
}
}

8
lib/index.js Normal file
View File

@ -0,0 +1,8 @@
/**
* @flow
*/
import firebase from './modules/core/firebase';
export type { default as User } from './modules/auth/User';
export default firebase;

View File

@ -1,6 +1,7 @@
import { NativeModules, Platform } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -23,8 +24,8 @@ export default class Interstitial {
this.admob = admob;
this.adUnit = adUnit;
this.loaded = false;
this.admob.removeAllListeners(`interstitial_${adUnit}`);
this.admob.on(`interstitial_${adUnit}`, this._onInterstitialEvent);
SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
SharedEventEmitter.addListener(`interstitial_${adUnit}`, this._onInterstitialEvent);
}
/**
@ -48,8 +49,8 @@ export default class Interstitial {
default:
}
this.admob.emit(eventType, emitData);
this.admob.emit(`interstitial:${this.adUnit}:*`, emitData);
SharedEventEmitter.emit(eventType, emitData);
SharedEventEmitter.emit(`interstitial:${this.adUnit}:*`, emitData);
};
/**
@ -97,7 +98,7 @@ export default class Interstitial {
return null;
}
const sub = this.admob.on(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
subscriptions.push(sub);
return sub;
}

View File

@ -1,6 +1,7 @@
import { NativeModules } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -18,8 +19,8 @@ export default class RewardedVideo {
this.admob = admob;
this.adUnit = adUnit;
this.loaded = false;
this.admob.removeAllListeners(`rewarded_video_${adUnit}`);
this.admob.on(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
SharedEventEmitter.addListener(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
}
/**
@ -43,8 +44,8 @@ export default class RewardedVideo {
default:
}
this.admob.emit(eventType, emitData);
this.admob.emit(`rewarded_video:${this.adUnit}:*`, emitData);
SharedEventEmitter.emit(eventType, emitData);
SharedEventEmitter.emit(`rewarded_video:${this.adUnit}:*`, emitData);
};
/**
@ -97,7 +98,7 @@ export default class RewardedVideo {
return null;
}
const sub = this.admob.on(`rewarded_video:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(`rewarded_video:${this.adUnit}:${eventType}`, listenerCb);
subscriptions.push(sub);
return sub;
}

View File

@ -1,4 +1,11 @@
import ModuleBase from './../../utils/ModuleBase';
/**
* @flow
* AdMob representation wrapper
*/
import { SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import ModuleBase from '../../utils/ModuleBase';
import Interstitial from './Interstitial';
import RewardedVideo from './RewardedVideo';
@ -12,72 +19,89 @@ import EventTypes, {
RewardedVideoEventTypes,
} from './EventTypes';
export default class AdMob extends ModuleBase {
static _NAMESPACE = 'admob';
static _NATIVE_MODULE = 'RNFirebaseAdMob';
import type App from '../core/firebase-app';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
type NativeEvent = {
adUnit: string,
payload: Object,
type: string,
}
const NATIVE_EVENTS = [
'interstitial_event',
'rewarded_video_event',
];
export const MODULE_NAME = 'RNFirebaseAdmob';
export const NAMESPACE = 'admob';
export default class AdMob extends ModuleBase {
_appId: ?string;
_initialized: boolean;
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
this._initialized = false;
this._appId = null;
this._eventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
this._eventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
SharedEventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
SharedEventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
}
_onInterstitialEvent(event) {
_onInterstitialEvent(event: NativeEvent): void {
const { adUnit } = event;
const jsEventType = `interstitial_${adUnit}`;
if (!this.hasListeners(jsEventType)) {
if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO
}
this.emit(jsEventType, event);
SharedEventEmitter.emit(jsEventType, event);
}
_onRewardedVideoEvent(event) {
_onRewardedVideoEvent(event: NativeEvent): void {
const { adUnit } = event;
const jsEventType = `rewarded_video_${adUnit}`;
if (!this.hasListeners(jsEventType)) {
if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO
}
this.emit(jsEventType, event);
SharedEventEmitter.emit(jsEventType, event);
}
initialize(appId: string) {
initialize(appId: string): void {
if (this._initialized) {
this.log.warn('AdMob has already been initialized!');
getLogger(this).warn('AdMob has already been initialized!');
} else {
this._initialized = true;
this._appId = appId;
this._native.initialize(appId);
getNativeModule(this).initialize(appId);
}
}
openDebugMenu() {
openDebugMenu(): void {
if (!this._initialized) {
this.log.warn('AdMob needs to be initialized before opening the dev menu!');
getLogger(this).warn('AdMob needs to be initialized before opening the dev menu!');
} else {
this.log.info('Opening debug menu');
this._native.openDebugMenu(this._appId);
getLogger(this).info('Opening debug menu');
getNativeModule(this).openDebugMenu(this._appId);
}
}
interstitial(adUnit: string) {
interstitial(adUnit: string): Interstitial {
return new Interstitial(this, adUnit);
}
rewarded(adUnit: string) {
rewarded(adUnit: string): RewardedVideo {
return new RewardedVideo(this, adUnit);
}
get namespace(): string {
return 'firebase:admob';
}
}
export const statics = {

View File

@ -1,5 +1,11 @@
// @flow
import ModuleBase from './../../utils/ModuleBase';
/**
* @flow
* Analytics representation wrapper
*/
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
const AlphaNumericUnderscore = /^[a-zA-Z0-9_]+$/;
@ -19,12 +25,16 @@ const ReservedEventNames = [
'user_engagement',
];
export default class Analytics extends ModuleBase {
static _NAMESPACE = 'analytics';
static _NATIVE_MODULE = 'RNFirebaseAnalytics';
export const MODULE_NAME = 'RNFirebaseAnalytics';
export const NAMESPACE = 'analytics';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options);
export default class Analytics extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
/**
@ -52,7 +62,7 @@ export default class Analytics extends ModuleBase {
// types are supported. String parameter values can be up to 36 characters long. The "firebase_"
// prefix is reserved and should not be used for parameter names.
return this._native.logEvent(name, params);
getNativeModule(this).logEvent(name, params);
}
/**
@ -60,7 +70,7 @@ export default class Analytics extends ModuleBase {
* @param enabled
*/
setAnalyticsCollectionEnabled(enabled: boolean): void {
return this._native.setAnalyticsCollectionEnabled(enabled);
getNativeModule(this).setAnalyticsCollectionEnabled(enabled);
}
/**
@ -69,7 +79,7 @@ export default class Analytics extends ModuleBase {
* @param screenClassOverride
*/
setCurrentScreen(screenName: string, screenClassOverride: string): void {
return this._native.setCurrentScreen(screenName, screenClassOverride);
getNativeModule(this).setCurrentScreen(screenName, screenClassOverride);
}
/**
@ -77,7 +87,7 @@ export default class Analytics extends ModuleBase {
* @param milliseconds
*/
setMinimumSessionDuration(milliseconds: number = 10000): void {
return this._native.setMinimumSessionDuration(milliseconds);
getNativeModule(this).setMinimumSessionDuration(milliseconds);
}
/**
@ -85,7 +95,7 @@ export default class Analytics extends ModuleBase {
* @param milliseconds
*/
setSessionTimeoutDuration(milliseconds: number = 1800000): void {
return this._native.setSessionTimeoutDuration(milliseconds);
getNativeModule(this).setSessionTimeoutDuration(milliseconds);
}
/**
@ -93,7 +103,7 @@ export default class Analytics extends ModuleBase {
* @param id
*/
setUserId(id: string): void {
return this._native.setUserId(id);
getNativeModule(this).setUserId(id);
}
/**
@ -102,7 +112,7 @@ export default class Analytics extends ModuleBase {
* @param value
*/
setUserProperty(name: string, value: string): void {
return this._native.setUserProperty(name, value);
getNativeModule(this).setUserProperty(name, value);
}
/**
@ -112,7 +122,9 @@ export default class Analytics extends ModuleBase {
*/
setUserProperties(object: Object): void {
for (const property of Object.keys(object)) {
this._native.setUserProperty(property, object[property]);
getNativeModule(this).setUserProperty(property, object[property]);
}
}
}
export const statics = {};

View File

@ -1,8 +1,13 @@
/**
* @url https://firebase.google.com/docs/reference/js/firebase.User
* @flow
* ConfirmationResult representation wrapper
*/
import { getNativeModule } from '../../utils/native';
import type Auth from './';
import type User from './User';
export default class ConfirmationResult {
_auth: Object;
_auth: Auth;
_verificationId: string;
/**
@ -10,7 +15,7 @@ export default class ConfirmationResult {
* @param auth
* @param verificationId The phone number authentication operation's verification ID.
*/
constructor(auth: Object, verificationId: string) {
constructor(auth: Auth, verificationId: string) {
this._auth = auth;
this._verificationId = verificationId;
}
@ -20,13 +25,11 @@ export default class ConfirmationResult {
* @param verificationCode
* @return {*}
*/
confirm(verificationCode: string): Promise<Object> {
return this._auth._interceptUserValue(
this._auth._native._confirmVerificationCode(verificationCode)
);
confirm(verificationCode: string): Promise<?User> {
return this._auth._interceptUserValue(getNativeModule(this._auth)._confirmVerificationCode(verificationCode));
}
get verificationId(): String | null {
get verificationId(): string | null {
return this._verificationId;
}
}

View File

@ -1,6 +1,10 @@
// @flow
import INTERNALS from './../../internals';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from './../../utils';
import INTERNALS from '../../utils/internals';
import { SharedEventEmitter } from '../../utils/events';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
type PhoneAuthSnapshot = {
state: 'sent' | 'timeout' | 'verified' | 'error',
@ -17,8 +21,7 @@ type PhoneAuthError = {
};
export default class PhoneAuthListener {
_auth: Object;
_auth: Auth;
_timeout: number;
_publicEvents: Object;
_internalEvents: Object;
@ -34,7 +37,7 @@ export default class PhoneAuthListener {
* @param phoneNumber
* @param timeout
*/
constructor(auth: Object, phoneNumber: string, timeout?: number) {
constructor(auth: Auth, phoneNumber: string, timeout?: number) {
this._auth = auth;
this._reject = null;
this._resolve = null;
@ -67,7 +70,7 @@ export default class PhoneAuthListener {
// start verification flow natively
if (isAndroid) {
this._auth._native.verifyPhoneNumber(
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
@ -75,7 +78,7 @@ export default class PhoneAuthListener {
}
if (isIOS) {
this._auth._native.verifyPhoneNumber(
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
);
@ -91,7 +94,8 @@ export default class PhoneAuthListener {
for (let i = 0, len = events.length; i < len; i++) {
const type = events[i];
this._auth.once(this._internalEvents[type], this[`_${type}Handler`].bind(this));
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
SharedEventEmitter.once(this._internalEvents[type], this[`_${type}Handler`].bind(this));
}
}
@ -101,7 +105,7 @@ export default class PhoneAuthListener {
* @private
*/
_addUserObserver(observer) {
this._auth.on(this._publicEvents.event, observer);
SharedEventEmitter.addListener(this._publicEvents.event, observer);
}
/**
@ -110,7 +114,7 @@ export default class PhoneAuthListener {
* @private
*/
_emitToObservers(snapshot: PhoneAuthSnapshot) {
this._auth.emit(this._publicEvents.event, snapshot);
SharedEventEmitter.emit(this._publicEvents.event, snapshot);
}
/**
@ -119,9 +123,9 @@ export default class PhoneAuthListener {
* @private
*/
_emitToErrorCb(snapshot) {
const error = snapshot.error;
const { error } = snapshot;
if (this._reject) this._reject(error);
this._auth.emit(this._publicEvents.error, error);
SharedEventEmitter.emit(this._publicEvents.error, error);
}
/**
@ -131,7 +135,7 @@ export default class PhoneAuthListener {
*/
_emitToSuccessCb(snapshot) {
if (this._resolve) this._resolve(snapshot);
this._auth.emit(this._publicEvents.success, snapshot);
SharedEventEmitter.emit(this._publicEvents.success, snapshot);
}
/**
@ -142,12 +146,12 @@ export default class PhoneAuthListener {
setTimeout(() => { // move to next event loop - not sure if needed
// internal listeners
Object.values(this._internalEvents).forEach((event) => {
this._auth.removeAllListeners(event);
SharedEventEmitter.removeAllListeners(event);
});
// user observer listeners
Object.values(this._publicEvents).forEach((publicEvent) => {
this._auth.removeAllListeners(publicEvent);
SharedEventEmitter.removeAllListeners(publicEvent);
});
}, 0);
}
@ -278,11 +282,11 @@ export default class PhoneAuthListener {
this._addUserObserver(observer);
if (isFunction(errorCb)) {
this._auth.once(this._publicEvents.error, errorCb);
SharedEventEmitter.once(this._publicEvents.error, errorCb);
}
if (isFunction(successCb)) {
this._auth.once(this._publicEvents.success, successCb);
SharedEventEmitter.once(this._publicEvents.success, successCb);
}
return this;
@ -294,6 +298,7 @@ export default class PhoneAuthListener {
*/
then(fn: () => PhoneAuthSnapshot) {
this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.then.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy
}
@ -304,6 +309,7 @@ export default class PhoneAuthListener {
*/
catch(fn: () => Error) {
this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.catch.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy
}

235
lib/modules/auth/User.js Normal file
View File

@ -0,0 +1,235 @@
/**
* @flow
* User representation wrapper
*/
import INTERNALS from '../../utils/internals';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
import type { ActionCodeSettings, AuthCredential } from '../../types';
type NativeUser = {
displayName?: string,
email?: string,
emailVerified?: boolean,
isAnonymous?: boolean,
phoneNumber?: string,
photoURL?: string,
providerData: UserInfo[],
providerId: string,
uid: string,
}
type UserInfo = {
displayName?: string,
email?: string,
phoneNumber?: string,
photoURL?: string,
providerId: string,
uid: string,
}
export default class User {
_auth: Auth;
_user: NativeUser;
/**
*
* @param auth Instance of Authentication class
* @param user user result object from native
*/
constructor(auth: Auth, user: NativeUser) {
this._auth = auth;
this._user = user;
}
/**
* PROPERTIES
*/
get displayName(): ?string {
return this._user.displayName || null;
}
get email(): ?string {
return this._user.email || null;
}
get emailVerified(): boolean {
return this._user.emailVerified || false;
}
get isAnonymous(): boolean {
return this._user.isAnonymous || false;
}
get phoneNumber(): ?string {
return this._user.phoneNumber || null;
}
get photoURL(): ?string {
return this._user.photoURL || null;
}
get providerData(): Array<UserInfo> {
return this._user.providerData;
}
get providerId(): string {
return this._user.providerId;
}
get uid(): string {
return this._user.uid;
}
/**
* METHODS
*/
/**
* Delete the current user
* @return {Promise}
*/
delete(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).delete());
}
/**
* get the token of current user
* @return {Promise}
*/
getIdToken(forceRefresh: boolean = false): Promise<string> {
return getNativeModule(this._auth).getToken(forceRefresh);
}
/**
*
* @param credential
*/
linkWithCredential(credential: AuthCredential): Promise<User> {
return this._auth
._interceptUserValue(getNativeModule(this._auth).link(credential.providerId, credential.token, credential.secret));
}
/**
* Re-authenticate a user with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
reauthenticateWithCredential(credential: AuthCredential): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).reauthenticate(credential.providerId, credential.token, credential.secret));
}
/**
* Reload the current user
* @return {Promise}
*/
reload(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).reload());
}
/**
* Send verification email to current user.
*/
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).sendEmailVerification(actionCodeSettings));
}
toJSON(): Object {
return Object.assign({}, this._user);
}
/**
*
* @param providerId
* @return {Promise.<TResult>|*}
*/
unlink(providerId: string): Promise<User> {
return this._auth._interceptUserValue(getNativeModule(this._auth).unlink(providerId));
}
/**
* Update the current user's email
*
* @param {string} email The user's _new_ email
* @return {Promise} A promise resolved upon completion
*/
updateEmail(email: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).updateEmail(email));
}
/**
* Update the current user's password
* @param {string} password the new password
* @return {Promise}
*/
updatePassword(password: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).updatePassword(password));
}
/**
* Update the current user's profile
* @param {Object} updates An object containing the keys listed [here](https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile)
* @return {Promise}
*/
updateProfile(updates: Object = {}): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).updateProfile(updates));
}
/**
* get the token of current user
* @deprecated Deprecated getToken in favor of getIdToken.
* @return {Promise}
*/
getToken(forceRefresh: boolean = false): Promise<Object> {
console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.');
return getNativeModule(this._auth).getToken(forceRefresh);
}
/**
* KNOWN UNSUPPORTED METHODS
*/
linkAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkAndRetrieveDataWithCredential'));
}
linkWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPhoneNumber'));
}
linkWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup'));
}
linkWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithRedirect'));
}
reauthenticateWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPhoneNumber'));
}
reauthenticateWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPopup'));
}
reauthenticateWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect'));
}
updatePhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'updatePhoneNumber'));
}
get refreshToken(): string {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken'));
}
}

View File

@ -1,7 +1,13 @@
// @flow
import User from './user';
import ModuleBase from './../../utils/ModuleBase';
import INTERNALS from './../../internals';
/**
* @flow
* Auth representation wrapper
*/
import User from './User';
import ModuleBase from '../../utils/ModuleBase';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import INTERNALS from '../../utils/internals';
import ConfirmationResult from './ConfirmationResult';
// providers
@ -14,43 +20,59 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener';
import type { ActionCodeSettings, AuthCredential } from '../../types';
import type App from '../core/firebase-app';
type AuthResult = {
authenticated: boolean,
user: Object|null
} | null;
const NATIVE_EVENTS = [
'auth_state_changed',
'phone_auth_state_changed',
];
export const MODULE_NAME = 'RNFirebaseAuth';
export const NAMESPACE = 'auth';
export default class Auth extends ModuleBase {
static _NAMESPACE = 'auth';
static _NATIVE_MODULE = 'RNFirebaseAuth';
_authResult: AuthResult | null;
_user: User | null;
_native: Object;
_getAppEventName: Function;
_authResult: AuthResultType | null;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._user = null;
this._authResult = null;
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onAuthStateChanged
this._getAppEventName('auth_state_changed'),
getAppEventName(this, 'auth_state_changed'),
this._onInternalAuthStateChanged.bind(this),
);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public events based on event.type
this._getAppEventName('phone_auth_state_changed'),
getAppEventName(this, 'phone_auth_state_changed'),
this._onInternalPhoneAuthStateChanged.bind(this),
);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onIdTokenChanged
this._getAppEventName('auth_id_token_changed'),
getAppEventName(this, 'auth_id_token_changed'),
this._onInternalIdTokenChanged.bind(this),
);
this._native.addAuthStateListener();
this._native.addIdTokenListener();
getNativeModule(this).addAuthStateListener();
getNativeModule(this).addIdTokenListener();
}
/**
@ -60,13 +82,13 @@ export default class Auth extends ModuleBase {
*/
_onInternalPhoneAuthStateChanged(event: Object) {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
this.emit(eventKey, event.state);
SharedEventEmitter.emit(eventKey, event.state);
}
_setAuthState(auth: AuthResultType) {
_setAuthState(auth: AuthResult) {
this._authResult = auth;
this._user = auth && auth.user ? new User(this, auth.user) : null;
this.emit(this._getAppEventName('onUserChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
}
/**
@ -74,9 +96,9 @@ export default class Auth extends ModuleBase {
* @param auth
* @private
*/
_onInternalAuthStateChanged(auth: AuthResultType) {
_onInternalAuthStateChanged(auth: AuthResult) {
this._setAuthState(auth);
this.emit(this._getAppEventName('onAuthStateChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user);
}
/**
@ -85,9 +107,9 @@ export default class Auth extends ModuleBase {
* @param emit
* @private
*/
_onInternalIdTokenChanged(auth: AuthResultType) {
_onInternalIdTokenChanged(auth: AuthResult) {
this._setAuthState(auth);
this.emit(this._getAppEventName('onIdTokenChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user);
}
/**
@ -97,8 +119,8 @@ export default class Auth extends ModuleBase {
* @returns {Promise.<*>}
* @private
*/
_interceptUserValue(promise) {
return promise.then((result) => {
_interceptUserValue(promise: Promise<AuthResult>): Promise<User> {
return promise.then((result: AuthResult) => {
if (!result) this._setAuthState(null);
else if (result.user) this._setAuthState(result);
else if (result.uid) this._setAuthState({ authenticated: true, user: result });
@ -106,6 +128,11 @@ export default class Auth extends ModuleBase {
});
}
_interceptUndefinedUserValue(promise: Promise<AuthResult>): Promise<void> {
return this._interceptUserValue(promise)
.then(() => {});
}
/*
* WEB API
*/
@ -115,8 +142,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onAuthStateChanged(listener: Function) {
this.log.info('Creating onAuthStateChanged listener');
this.on(this._getAppEventName('onAuthStateChanged'), listener);
getLogger(this).info('Creating onAuthStateChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onAuthStateChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offAuthStateChanged.bind(this, listener);
}
@ -126,8 +153,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offAuthStateChanged(listener: Function) {
this.log.info('Removing onAuthStateChanged listener');
this.removeListener(this._getAppEventName('onAuthStateChanged'), listener);
getLogger(this).info('Removing onAuthStateChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener);
}
/**
@ -135,8 +162,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onIdTokenChanged(listener: Function) {
this.log.info('Creating onIdTokenChanged listener');
this.on(this._getAppEventName('onIdTokenChanged'), listener);
getLogger(this).info('Creating onIdTokenChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onIdTokenChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offIdTokenChanged.bind(this, listener);
}
@ -146,8 +173,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offIdTokenChanged(listener: Function) {
this.log.info('Removing onIdTokenChanged listener');
this.removeListener(this._getAppEventName('onIdTokenChanged'), listener);
getLogger(this).info('Removing onIdTokenChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener);
}
/**
@ -155,8 +182,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onUserChanged(listener: Function) {
this.log.info('Creating onUserChanged listener');
this.on(this._getAppEventName('onUserChanged'), listener);
getLogger(this).info('Creating onUserChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onUserChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offUserChanged.bind(this, listener);
}
@ -166,24 +193,24 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offUserChanged(listener: Function) {
this.log.info('Removing onUserChanged listener');
this.removeListener(this._getAppEventName('onUserChanged'), listener);
getLogger(this).info('Removing onUserChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener);
}
/**
* Sign the current user out
* @return {Promise}
*/
signOut(): Promise<null> {
return this._interceptUserValue(this._native.signOut());
signOut(): Promise<void> {
return this._interceptUndefinedUserValue(getNativeModule(this).signOut());
}
/**
* Sign a user in anonymously
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously(): Promise<Object> {
return this._interceptUserValue(this._native.signInAnonymously());
signInAnonymously(): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInAnonymously());
}
/**
@ -192,8 +219,8 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
*/
createUserWithEmailAndPassword(email: string, password: string): Promise<Object> {
return this._interceptUserValue(this._native.createUserWithEmailAndPassword(email, password));
createUserWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).createUserWithEmailAndPassword(email, password));
}
/**
@ -202,8 +229,8 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
*/
signInWithEmailAndPassword(email: string, password: string): Promise<Object> {
return this._interceptUserValue(this._native.signInWithEmailAndPassword(email, password));
signInWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInWithEmailAndPassword(email, password));
}
/**
@ -211,17 +238,17 @@ export default class Auth extends ModuleBase {
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInWithCustomToken(customToken: string): Promise<Object> {
return this._interceptUserValue(this._native.signInWithCustomToken(customToken));
signInWithCustomToken(customToken: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInWithCustomToken(customToken));
}
/**
* Sign the user in with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
signInWithCredential(credential: CredentialType): Promise<Object> {
signInWithCredential(credential: AuthCredential): Promise<User> {
return this._interceptUserValue(
this._native.signInWithCredential(
getNativeModule(this).signInWithCredential(
credential.providerId, credential.token, credential.secret,
),
);
@ -231,8 +258,8 @@ export default class Auth extends ModuleBase {
* Asynchronously signs in using a phone number.
*
*/
signInWithPhoneNumber(phoneNumber: string): Promise<Object> {
return this._native.signInWithPhoneNumber(phoneNumber).then((result) => {
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return getNativeModule(this).signInWithPhoneNumber(phoneNumber).then((result) => {
return new ConfirmationResult(this, result.verificationId);
});
}
@ -254,8 +281,8 @@ export default class Auth extends ModuleBase {
* Send reset password instructions via email
* @param {string} email The email to send password reset instructions
*/
sendPasswordResetEmail(email: string): Promise<Object> {
return this._native.sendPasswordResetEmail(email);
sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void> {
return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings);
}
/**
@ -266,8 +293,8 @@ export default class Auth extends ModuleBase {
* @param newPassword
* @return {Promise.<Null>}
*/
confirmPasswordReset(code: string, newPassword: string): Promise<null> {
return this._native.confirmPasswordReset(code, newPassword);
confirmPasswordReset(code: string, newPassword: string): Promise<void> {
return getNativeModule(this).confirmPasswordReset(code, newPassword);
}
/**
@ -277,8 +304,8 @@ export default class Auth extends ModuleBase {
* @param code
* @return {Promise.<Null>}
*/
applyActionCode(code: string): Promise<any> {
return this._native.applyActionCode(code);
applyActionCode(code: string): Promise<void> {
return getNativeModule(this).applyActionCode(code);
}
/**
@ -288,16 +315,16 @@ export default class Auth extends ModuleBase {
* @param code
* @return {Promise.<any>|Promise<ActionCodeInfo>}
*/
checkActionCode(code: string): Promise<any> {
return this._native.checkActionCode(code);
checkActionCode(code: string): Promise<void> {
return getNativeModule(this).checkActionCode(code);
}
/**
* Get the currently signed in user
* @return {Promise}
*/
getCurrentUser(): Promise<Object> {
return this._interceptUserValue(this._native.getCurrentUser());
getCurrentUser(): Promise<User | null> {
return this._interceptUserValue(getNativeModule(this).getCurrentUser());
}
/**
@ -305,7 +332,7 @@ export default class Auth extends ModuleBase {
* @return {Promise}
*/
fetchProvidersForEmail(email: string): Promise<Array<String>> {
return this._native.fetchProvidersForEmail(email);
return getNativeModule(this).fetchProvidersForEmail(email);
}
/**
@ -316,32 +343,28 @@ export default class Auth extends ModuleBase {
return this._user;
}
get namespace(): string {
return 'firebase:auth';
}
/**
* KNOWN UNSUPPORTED METHODS
*/
getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'getRedirectResult'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult'));
}
setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'setPersistence'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence'));
}
signInAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInAndRetrieveDataWithCredential'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInAndRetrieveDataWithCredential'));
}
signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInWithPopup'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup'));
}
signInWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInWithRedirect'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithRedirect'));
}
}

View File

@ -1,3 +1,9 @@
/**
* @flow
* EmailAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'password';
export default class EmailAuthProvider {
@ -5,11 +11,11 @@ export default class EmailAuthProvider {
throw new Error('`new EmailAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(email, password) {
static credential(email: string, password: string): AuthCredential {
return {
token: email,
secret: password,

View File

@ -1,3 +1,9 @@
/**
* @flow
* FacebookAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'facebook.com';
export default class FacebookAuthProvider {
@ -5,11 +11,11 @@ export default class FacebookAuthProvider {
throw new Error('`new FacebookAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(token) {
static credential(token: string): AuthCredential {
return {
token,
secret: '',

View File

@ -1,3 +1,9 @@
/**
* @flow
* GithubAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'github.com';
export default class GithubAuthProvider {
@ -5,11 +11,11 @@ export default class GithubAuthProvider {
throw new Error('`new GithubAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(token) {
static credential(token: string): AuthCredential {
return {
token,
secret: '',

View File

@ -1,3 +1,9 @@
/**
* @flow
* EmailAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'google.com';
export default class GoogleAuthProvider {
@ -5,11 +11,11 @@ export default class GoogleAuthProvider {
throw new Error('`new GoogleAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(token, secret) {
static credential(token: string, secret: string): AuthCredential {
return {
token,
secret,

View File

@ -1,3 +1,9 @@
/**
* @flow
* PhoneAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'phone';
export default class PhoneAuthProvider {
@ -5,11 +11,11 @@ export default class PhoneAuthProvider {
throw new Error('`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(verificationId, code) {
static credential(verificationId: string, code: string): AuthCredential {
return {
token: verificationId,
secret: code,

View File

@ -1,3 +1,9 @@
/**
* @flow
* TwitterAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'twitter.com';
export default class TwitterAuthProvider {
@ -5,11 +11,11 @@ export default class TwitterAuthProvider {
throw new Error('`new TwitterAuthProvider()` is not supported on the native Firebase SDKs.');
}
static get PROVIDER_ID() {
static get PROVIDER_ID(): string {
return providerId;
}
static credential(token, secret) {
static credential(token: string, secret: string): AuthCredential {
return {
token,
secret,

View File

@ -1,229 +0,0 @@
import INTERNALS from './../../internals';
/**
* @url https://firebase.google.com/docs/reference/js/firebase.User
*/
export default class User {
/**
*
* @param authClass Instance of Authentication class
* @param user user result object from native
*/
constructor(authClass, userObj) {
this._auth = authClass;
this._user = userObj;
}
/**
* INTERNALS
*/
/**
* Returns a user property or null if does not exist
* @param prop
* @returns {*}
* @private
*/
_valueOrNull(prop) {
if (!Object.hasOwnProperty.call(this._user, prop)) return null;
return this._user[prop];
}
/**
* Returns a user property or false if does not exist
* @param prop
* @returns {*}
* @private
*/
_valueOrFalse(prop) {
if (!Object.hasOwnProperty.call(this._user, prop)) return false;
return this._user[prop];
}
/**
* PROPERTIES
*/
get displayName(): String | null {
return this._valueOrNull('displayName');
}
get email(): String | null {
return this._valueOrNull('email');
}
get emailVerified(): Boolean {
return this._valueOrNull('emailVerified');
}
get isAnonymous(): Boolean {
return this._valueOrFalse('isAnonymous');
}
get phoneNumber(): String | null {
return this._valueOrNull('phoneNumber');
}
get photoURL(): String | null {
return this._valueOrNull('photoURL');
}
get providerId() {
return this._valueOrNull('providerId');
}
get uid(): String {
return this._valueOrNull('uid');
}
// noinspection ReservedWordAsName
/**
* METHODS
*/
toJSON() {
return Object.assign({}, this._user);
}
/**
* Delete the current user
* @return {Promise}
*/
delete(): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.delete());
}
/**
*
* @param credential
*/
linkWithCredential(credential: CredentialType) {
return this._auth._interceptUserValue(this._auth._native.link(credential.providerId, credential.token, credential.secret));
}
/**
*
* @param providerId
* @return {Promise.<TResult>|*}
*/
unlink(providerId: string) {
return this._auth._interceptUserValue(this._auth._native.unlink(providerId));
}
/**
* Re-authenticate a user with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
reauthenticateWithCredential(credential: CredentialType) {
return this._auth._interceptUserValue(this._auth._native.reauthenticate(credential.providerId, credential.token, credential.secret));
}
/**
* Reload the current user
* @return {Promise}
*/
reload(): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.reload());
}
/**
* get the token of current user
* @deprecated Deprecated getToken in favor of getIdToken.
* @return {Promise}
*/
getToken(forceRefresh: Boolean = false): Promise<Object> {
console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.');
return this._auth._native.getToken(forceRefresh);
}
/**
* get the token of current user
* @return {Promise}
*/
getIdToken(forceRefresh: Boolean = false): Promise<Object> {
return this._auth._native.getToken(forceRefresh);
}
/**
*
* @returns {Array}
*/
get providerData(): Array {
return this._valueOrNull('providerData') || [];
}
/**
* Update the current user's email
*
* @param {string} email The user's _new_ email
* @return {Promise} A promise resolved upon completion
*/
updateEmail(email: string): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.updateEmail(email));
}
/**
* Update the current user's profile
* @param {Object} updates An object containing the keys listed [here](https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile)
* @return {Promise}
*/
updateProfile(updates: Object = {}): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.updateProfile(updates));
}
/**
* Update the current user's password
* @param {string} password the new password
* @return {Promise}
*/
updatePassword(password: string): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.updatePassword(password));
}
/**
* Send verification email to current user.
*/
sendEmailVerification(): Promise<Object> {
return this._auth._interceptUserValue(this._auth._native.sendEmailVerification());
}
/**
* KNOWN UNSUPPORTED METHODS
*/
linkAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkAndRetrieveDataWithCredential'));
}
linkWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPhoneNumber'));
}
linkWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup'));
}
linkWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithRedirect'));
}
reauthenticateWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPhoneNumber'));
}
reauthenticateWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPopup'));
}
reauthenticateWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect'));
}
updatePhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'updatePhoneNumber'));
}
get refreshToken() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken'));
}
}

View File

@ -1,21 +0,0 @@
/**
* @flow
*/
// todo move out
export class ReferenceBase extends Base {
constructor(path: string) {
super();
this.path = path || '/';
}
/**
* The last part of a Reference's path (after the last '/')
* The key of a root Reference is null.
* @type {String}
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key}
*/
get key(): string | null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
}
}

View File

@ -1,18 +1,37 @@
/**
* @flow
* Remote Config representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
type NativeValue = {
stringValue?: string,
numberValue?: number,
dataValue?: Object,
boolValue?: boolean,
source: 'remoteConfigSourceRemote' | 'remoteConfigSourceDefault' | ' remoteConfigSourceStatic',
}
export const MODULE_NAME = 'RNFirebaseRemoteConfig';
export const NAMESPACE = 'config';
/**
* @class Config
*/
export default class RemoteConfig extends ModuleBase {
static _NAMESPACE = 'config';
static _NATIVE_MODULE = 'RNFirebaseRemoteConfig';
_developerModeEnabled: boolean;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options);
this.developerModeEnabled = false;
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;
}
/**
@ -21,12 +40,12 @@ export default class RemoteConfig extends ModuleBase {
* @returns {*}
* @private
*/
_nativeValueToJS(nativeValue) {
_nativeValueToJS(nativeValue: NativeValue) {
return {
source: nativeValue.source,
val() {
if (nativeValue.boolValue !== null && (nativeValue.stringValue === 'true' || nativeValue.stringValue === 'false' || nativeValue.stringValue === null)) return nativeValue.boolValue;
if (nativeValue.numberValue !== null && (nativeValue.stringValue == null || nativeValue.stringValue === '' || `${nativeValue.numberValue}` === nativeValue.stringValue)) return nativeValue.numberValue;
if (nativeValue.numberValue !== null && nativeValue.numberValue !== undefined && (nativeValue.stringValue == null || nativeValue.stringValue === '' || nativeValue.numberValue.toString() === nativeValue.stringValue)) return nativeValue.numberValue;
if (nativeValue.dataValue !== nativeValue.stringValue && (nativeValue.stringValue == null || nativeValue.stringValue === '')) return nativeValue.dataValue;
return nativeValue.stringValue;
},
@ -37,10 +56,10 @@ export default class RemoteConfig extends ModuleBase {
* Enable Remote Config developer mode to allow for frequent refreshes of the cache
*/
enableDeveloperMode() {
if (!this.developerModeEnabled) {
this.log.debug('Enabled developer mode');
this._native.enableDeveloperMode();
this.developerModeEnabled = true;
if (!this._developerModeEnabled) {
getLogger(this).debug('Enabled developer mode');
getNativeModule(this).enableDeveloperMode();
this._developerModeEnabled = true;
}
}
@ -51,11 +70,11 @@ export default class RemoteConfig extends ModuleBase {
*/
fetch(expiration?: number) {
if (expiration !== undefined) {
this.log.debug(`Fetching remote config data with expiration ${expiration.toString()}`);
return this._native.fetchWithExpirationDuration(expiration);
getLogger(this).debug(`Fetching remote config data with expiration ${expiration.toString()}`);
return getNativeModule(this).fetchWithExpirationDuration(expiration);
}
this.log.debug('Fetching remote config data');
return this._native.fetch();
getLogger(this).debug('Fetching remote config data');
return getNativeModule(this).fetch();
}
/**
@ -65,8 +84,8 @@ export default class RemoteConfig extends ModuleBase {
* rejects if no Fetched Config was found, or the Fetched Config was already activated.
*/
activateFetched() {
this.log.debug('Activating remote config');
return this._native.activateFetched();
getLogger(this).debug('Activating remote config');
return getNativeModule(this).activateFetched();
}
/**
@ -83,7 +102,7 @@ export default class RemoteConfig extends ModuleBase {
* }
*/
getValue(key: String) {
return this._native
return getNativeModule(this)
.getValue(key || '')
.then(this._nativeValueToJS);
}
@ -103,7 +122,7 @@ export default class RemoteConfig extends ModuleBase {
* }
*/
getValues(keys: Array<String>) {
return this._native
return getNativeModule(this)
.getValues(keys || [])
.then((nativeValues) => {
const values: { [String]: Object } = {};
@ -120,7 +139,7 @@ export default class RemoteConfig extends ModuleBase {
* @returns {*|Promise.<Array<String>>}
*/
getKeysByPrefix(prefix?: String) {
return this._native.getKeysByPrefix(prefix);
return getNativeModule(this).getKeysByPrefix(prefix);
}
/**
@ -128,7 +147,7 @@ export default class RemoteConfig extends ModuleBase {
* @param defaults: A dictionary mapping a String key to a Object values.
*/
setDefaults(defaults: Object) {
this._native.setDefaults(defaults);
getNativeModule(this).setDefaults(defaults);
}
/**
@ -136,6 +155,8 @@ export default class RemoteConfig extends ModuleBase {
* @param resource: The plist file name or resource ID
*/
setDefaultsFromResource(resource: String | number) {
this._native.setDefaultsFromResource(resource);
getNativeModule(this).setDefaultsFromResource(resource);
}
}
export const statics = {};

View File

@ -0,0 +1,159 @@
/*
* @flow
*/
import { NativeModules } from 'react-native';
import APPS from '../../utils/apps';
import { SharedEventEmitter } from '../../utils/events';
import INTERNALS from '../../utils/internals';
import { isObject } from '../../utils';
import AdMob, { NAMESPACE as AdmobNamespace } from '../admob';
import Auth, { NAMESPACE as AuthNamespace } from '../auth';
import Analytics, { NAMESPACE as AnalyticsNamespace } from '../analytics';
import Config, { NAMESPACE as ConfigNamespace } from '../config';
import Crash, { NAMESPACE as CrashNamespace } from '../crash';
import Crashlytics, { NAMESPACE as CrashlyticsNamespace } from '../fabric/crashlytics';
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
import Links, { NAMESPACE as LinksNamespace } from '../links';
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
import Performance, { NAMESPACE as PerfNamespace } from '../perf';
import Storage, { NAMESPACE as StorageNamespace } from '../storage';
import Utils, { NAMESPACE as UtilsNamespace } from '../utils';
import type {
FirebaseOptions,
} from '../../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class App {
_extendedProps: { [string] : boolean };
_initialized: boolean = false;
_name: string;
_nativeInitialized: boolean = false;
_options: FirebaseOptions;
admob: () => AdMob;
analytics: () => Analytics;
auth: () => Auth;
config: () => Config;
crash: () => Crash;
database: () => Database;
fabric: {
crashlytics: () => Crashlytics,
};
firestore: () => Firestore;
links: () => Links;
messaging: () => Messaging;
perf: () => Performance;
storage: () => Storage;
utils: () => Utils;
constructor(name: string, options: FirebaseOptions, fromNative: boolean = false) {
this._name = name;
this._options = Object.assign({}, options);
if (fromNative) {
this._initialized = true;
this._nativeInitialized = true;
} else if (options.databaseURL && options.apiKey) {
FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => {
this._initialized = true;
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
});
}
// modules
this.admob = APPS.appModule(this, AdmobNamespace, AdMob);
this.analytics = APPS.appModule(this, AnalyticsNamespace, Analytics);
this.auth = APPS.appModule(this, AuthNamespace, Auth);
this.config = APPS.appModule(this, ConfigNamespace, Config);
this.crash = APPS.appModule(this, CrashNamespace, Crash);
this.database = APPS.appModule(this, DatabaseNamespace, Database);
this.fabric = {
crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics),
};
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
this.links = APPS.appModule(this, LinksNamespace, Links);
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
this.perf = APPS.appModule(this, PerfNamespace, Performance);
this.storage = APPS.appModule(this, StorageNamespace, Storage);
this.utils = APPS.appModule(this, UtilsNamespace, Utils);
this._extendedProps = {};
}
/**
*
* @return {*}
*/
get name(): string {
return this._name;
}
/**
*
* @return {*}
*/
get options(): FirebaseOptions {
return Object.assign({}, this._options);
}
/**
* Undocumented firebase web sdk method that allows adding additional properties onto
* a firebase app instance.
*
* See: https://github.com/firebase/firebase-js-sdk/blob/master/tests/app/firebase_app.test.ts#L328
*
* @param props
*/
extendApp(props: Object) {
if (!isObject(props)) throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG('Object', 'extendApp'));
const keys = Object.keys(props);
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) {
throw new Error(INTERNALS.STRINGS.ERROR_PROTECTED_PROP(key));
}
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
this[key] = props[key];
this._extendedProps[key] = true;
}
}
/**
*
* @return {Promise}
*/
delete() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete'));
// TODO only the ios sdk currently supports delete, add back in when android also supports it
// if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
// return Promise.reject(
// new Error('Unable to delete the default native firebase app instance.'),
// );
// }
//
// return FirebaseCoreModule.deleteApp(this._name);
}
/**
*
* @return {*}
*/
onReady(): Promise<App> {
if (this._initialized) return Promise.resolve(this);
return new Promise((resolve, reject) => {
SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
if (error) return reject(new Error(error)); // error is a string as it's from native
return resolve(this); // return app
});
});
}
}

View File

@ -0,0 +1,126 @@
/**
* @providesModule Firebase
* @flow
*/
import { NativeModules } from 'react-native';
import APPS from '../../utils/apps';
import INTERNALS from '../../utils/internals';
import App from './firebase-app';
import VERSION from '../../version';
// module imports
import { statics as AdMobStatics, MODULE_NAME as AdmobModuleName } from '../admob';
import { statics as AuthStatics, MODULE_NAME as AuthModuleName } from '../auth';
import { statics as AnalyticsStatics, MODULE_NAME as AnalyticsModuleName } from '../analytics';
import { statics as ConfigStatics, MODULE_NAME as ConfigModuleName } from '../config';
import { statics as CrashStatics, MODULE_NAME as CrashModuleName } from '../crash';
import { statics as CrashlyticsStatics, MODULE_NAME as CrashlyticsModuleName } from '../fabric/crashlytics';
import { statics as DatabaseStatics, MODULE_NAME as DatabaseModuleName } from '../database';
import { statics as FirestoreStatics, MODULE_NAME as FirestoreModuleName } from '../firestore';
import { statics as LinksStatics, MODULE_NAME as LinksModuleName } from '../links';
import { statics as MessagingStatics, MODULE_NAME as MessagingModuleName } from '../messaging';
import { statics as PerformanceStatics, MODULE_NAME as PerfModuleName } from '../perf';
import { statics as StorageStatics, MODULE_NAME as StorageModuleName } from '../storage';
import { statics as UtilsStatics, MODULE_NAME as UtilsModuleName } from '../utils';
import type {
AdMobModule,
AnalyticsModule,
AuthModule,
ConfigModule,
CrashModule,
DatabaseModule,
FabricModule,
FirebaseOptions,
FirestoreModule,
LinksModule,
MessagingModule,
PerformanceModule,
StorageModule,
UtilsModule,
} from '../../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
class Firebase {
admob: AdMobModule;
analytics: AnalyticsModule;
auth: AuthModule;
config: ConfigModule;
crash: CrashModule;
database: DatabaseModule;
fabric: FabricModule;
firestore: FirestoreModule;
links: LinksModule;
messaging: MessagingModule;
perf: PerformanceModule;
storage: StorageModule;
utils: UtilsModule;
constructor() {
if (!FirebaseCoreModule) {
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
}
APPS.initializeNativeApps();
// modules
this.admob = APPS.moduleAndStatics('admob', AdMobStatics, AdmobModuleName);
this.analytics = APPS.moduleAndStatics('analytics', AnalyticsStatics, AnalyticsModuleName);
this.auth = APPS.moduleAndStatics('auth', AuthStatics, AuthModuleName);
this.config = APPS.moduleAndStatics('config', ConfigStatics, ConfigModuleName);
this.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
this.database = APPS.moduleAndStatics('database', DatabaseStatics, DatabaseModuleName);
this.fabric = {
crashlytics: APPS.moduleAndStatics('crashlytics', CrashlyticsStatics, CrashlyticsModuleName),
};
this.firestore = APPS.moduleAndStatics('firestore', FirestoreStatics, FirestoreModuleName);
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
this.messaging = APPS.moduleAndStatics('messaging', MessagingStatics, MessagingModuleName);
this.perf = APPS.moduleAndStatics('perf', PerformanceStatics, PerfModuleName);
this.storage = APPS.moduleAndStatics('storage', StorageStatics, StorageModuleName);
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName);
}
/**
* Web SDK initializeApp
*
* @param options
* @param name
* @return {*}
*/
initializeApp(options: FirebaseOptions, name: string): App {
return APPS.initializeApp(options, name);
}
/**
* Retrieves a Firebase app instance.
*
* When called with no arguments, the default app is returned.
* When an app name is provided, the app corresponding to that name is returned.
*
* @param name
* @return {*}
*/
app(name?: string): App {
return APPS.app(name);
}
/**
* A (read-only) array of all initialized apps.
* @return {Array}
*/
get apps(): Array<App> {
return APPS.apps();
}
/**
* The current SDK version.
* @return {string}
*/
get SDK_VERSION(): string {
return VERSION;
}
}
export default new Firebase();

View File

@ -1,12 +1,23 @@
// @flow
import ModuleBase from './../../utils/ModuleBase';
/**
* @flow
* Crash Reporting representation wrapper
*/
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
import type { FirebaseError } from '../../types';
export const MODULE_NAME = 'RNFirebaseCrash';
export const NAMESPACE = 'crash';
export default class Crash extends ModuleBase {
static _NAMESPACE = 'crash';
static _NATIVE_MODULE = 'RNFirebaseCrash';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options);
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
/**
@ -14,7 +25,7 @@ export default class Crash extends ModuleBase {
* @param enabled
*/
setCrashCollectionEnabled(enabled: boolean): void {
this._native.setCrashCollectionEnabled(enabled);
getNativeModule(this).setCrashCollectionEnabled(enabled);
}
/**
@ -22,7 +33,7 @@ export default class Crash extends ModuleBase {
* @returns {Promise.<boolean>}
*/
isCrashCollectionEnabled(): Promise<boolean> {
return this._native.isCrashCollectionEnabled();
return getNativeModule(this).isCrashCollectionEnabled();
}
/**
@ -30,7 +41,7 @@ export default class Crash extends ModuleBase {
* @param {string} message
*/
log(message: string): void {
this._native.log(message);
getNativeModule(this).log(message);
}
/**
@ -41,7 +52,7 @@ export default class Crash extends ModuleBase {
* @param {string} tag
*/
logcat(level: number, tag: string, message: string): void {
this._native.logcat(level, tag, message);
getNativeModule(this).logcat(level, tag, message);
}
/**
@ -68,6 +79,8 @@ export default class Crash extends ModuleBase {
errorMessage = `${errorMessage} - ${stackRows[i]}\r\n`;
}
this._native.report(errorMessage);
getNativeModule(this).report(errorMessage);
}
}
export const statics = {};

View File

@ -1,7 +1,11 @@
/* @flow */
import { typeOf } from './../../utils';
import Reference from './reference';
/**
* @flow
* Disconnect representation wrapper
*/
import { typeOf } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Database from './';
import type Reference from './reference';
/**
@ -9,6 +13,7 @@ import Reference from './reference';
* @class Disconnect
*/
export default class Disconnect {
_database: Database;
ref: Reference;
path: string;
@ -28,7 +33,7 @@ export default class Disconnect {
* @returns {*}
*/
set(value: string | Object): Promise<void> {
return this._database._native.onDisconnectSet(this.path, { type: typeOf(value), value });
return getNativeModule(this._database).onDisconnectSet(this.path, { type: typeOf(value), value });
}
/**
@ -37,7 +42,7 @@ export default class Disconnect {
* @returns {*}
*/
update(values: Object): Promise<void> {
return this._database._native.onDisconnectUpdate(this.path, values);
return getNativeModule(this._database).onDisconnectUpdate(this.path, values);
}
/**
@ -45,7 +50,7 @@ export default class Disconnect {
* @returns {*}
*/
remove(): Promise<void> {
return this._database._native.onDisconnectRemove(this.path);
return getNativeModule(this._database).onDisconnectRemove(this.path);
}
/**
@ -53,6 +58,6 @@ export default class Disconnect {
* @returns {*}
*/
cancel(): Promise<void> {
return this._database._native.onDisconnectCancel(this.path);
return getNativeModule(this._database).onDisconnectCancel(this.path);
}
}

View File

@ -6,21 +6,38 @@ import { NativeModules } from 'react-native';
import Reference from './reference';
import TransactionHandler from './transaction';
import ModuleBase from './../../utils/ModuleBase';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
const NATIVE_EVENTS = [
'database_transaction_event',
// 'database_server_offset', // TODO
];
export const MODULE_NAME = 'RNFirebaseDatabase';
export const NAMESPACE = 'database';
/**
* @class Database
*/
export default class Database extends ModuleBase {
static _NAMESPACE = 'database';
static _NATIVE_MODULE = 'RNFirebaseDatabase';
_offsetRef: Reference;
_serverTimeOffset: number;
_transactionHandler: TransactionHandler;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App, options: Object = {}) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._transactionHandler = new TransactionHandler(this);
if (this._options.persistence) {
this._native.setPersistence(this._options.persistence);
if (options.persistence) {
getNativeModule(this).setPersistence(options.persistence);
}
// server time listener
@ -40,22 +57,22 @@ export default class Database extends ModuleBase {
*
* @return {number}
*/
getServerTime() {
getServerTime(): number {
return new Date(Date.now() + this._serverTimeOffset);
}
/**
*
*/
goOnline() {
this._native.goOnline();
goOnline(): void {
getNativeModule(this).goOnline();
}
/**
*
*/
goOffline() {
this._native.goOffline();
goOffline(): void {
getNativeModule(this).goOffline();
}
/**
@ -63,7 +80,7 @@ export default class Database extends ModuleBase {
* @param path
* @returns {Reference}
*/
ref(path: string) {
ref(path: string): Reference {
return new Reference(this, path);
}
}
@ -72,9 +89,9 @@ export const statics = {
ServerValue: NativeModules.RNFirebaseDatabase ? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
} : {},
enableLogging(bool) {
if (NativeModules[Database._NATIVE_MODULE]) {
NativeModules[Database._NATIVE_MODULE].enableLogging(bool);
enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);
}
},
};

View File

@ -1,9 +1,11 @@
/**
* @flow
* Query representation wrapper
*/
import { objectToUniqueId } from '../../utils';
import Reference from './reference.js';
import { objectToUniqueId } from './../../utils';
import type { DatabaseModifier } from '../../types';
import type Reference from './reference.js';
// todo doc methods
@ -11,6 +13,7 @@ import { objectToUniqueId } from './../../utils';
* @class Query
*/
export default class Query {
_reference: Reference;
modifiers: Array<DatabaseModifier>;
constructor(ref: Reference, path: string, existingModifiers?: Array<DatabaseModifier>) {
@ -26,7 +29,7 @@ export default class Query {
*/
orderBy(name: string, key?: string) {
this.modifiers.push({
id: `orderBy-${name}:${key}`,
id: `orderBy-${name}:${key || ''}`,
type: 'orderBy',
name,
key,
@ -61,7 +64,7 @@ export default class Query {
*/
filter(name: string, value: any, key?: string) {
this.modifiers.push({
id: `filter-${name}:${objectToUniqueId(value)}:${key}`,
id: `filter-${name}:${objectToUniqueId(value)}:${key || ''}`,
type: 'filter',
name,
value,

View File

@ -1,11 +1,13 @@
/**
* @flow
* Database Reference representation wrapper
*/
import Query from './query.js';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import ReferenceBase from './../../utils/ReferenceBase';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import ReferenceBase from '../../utils/ReferenceBase';
import {
promiseOrCallback,
@ -15,9 +17,12 @@ import {
tryJSONParse,
tryJSONStringify,
generatePushID,
} from './../../utils';
} from '../../utils';
import INTERNALS from './../../internals';
import SyncTree from '../../utils/SyncTree';
import type Database from './';
import type { DatabaseModifier, FirebaseError } from '../../types';
// track all event registrations by path
let listeners = 0;
@ -35,6 +40,13 @@ const ReferenceEventTypes = {
child_moved: 'child_moved',
};
type DatabaseListener = {
listenerId: number;
eventName: string;
successCallback: Function;
failureCallback?: Function;
}
/**
* @typedef {String} ReferenceLocation - Path to location in the database, relative
* to the root reference. Consists of a path where segments are separated by a
@ -63,18 +75,18 @@ const ReferenceEventTypes = {
* @extends ReferenceBase
*/
export default class Reference extends ReferenceBase {
_refListeners: { [listenerId: number]: DatabaseListener };
_database: Object;
_database: Database;
_promise: ?Promise<*>;
_query: Query;
_refListeners: { [listenerId: number]: DatabaseListener };
constructor(database: Object, path: string, existingModifiers?: Array<DatabaseModifier>) {
super(path, database);
constructor(database: Database, path: string, existingModifiers?: Array<DatabaseModifier>) {
super(path);
this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, path, existingModifiers);
this.log.debug('Created new Reference', this._getRefKey());
getLogger(database).debug('Created new Reference', this._getRefKey());
}
/**
@ -87,8 +99,8 @@ export default class Reference extends ReferenceBase {
* @param bool
* @returns {*}
*/
keepSynced(bool: boolean) {
return this._database._native.keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
keepSynced(bool: boolean): Promise<void> {
return getNativeModule(this._database).keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
}
/**
@ -99,9 +111,9 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
set(value: any, onComplete?: Function): Promise {
set(value: any, onComplete?: Function): Promise<void> {
return promiseOrCallback(
this._database._native.set(this.path, this._serializeAnyType(value)),
getNativeModule(this._database).set(this.path, this._serializeAnyType(value)),
onComplete,
);
}
@ -114,11 +126,11 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setPriority(priority: string | number | null, onComplete?: Function): Promise {
setPriority(priority: string | number | null, onComplete?: Function): Promise<void> {
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
this._database._native.setPriority(this.path, _priority),
getNativeModule(this._database).setPriority(this.path, _priority),
onComplete,
);
}
@ -132,12 +144,12 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setWithPriority(value: any, priority: string | number | null, onComplete?: Function): Promise {
setWithPriority(value: any, priority: string | number | null, onComplete?: Function): Promise<void> {
const _value = this._serializeAnyType(value);
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
this._database._native.setWithPriority(this.path, _value, _priority),
getNativeModule(this._database).setWithPriority(this.path, _value, _priority),
onComplete,
);
}
@ -150,11 +162,11 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
update(val: Object, onComplete?: Function): Promise {
update(val: Object, onComplete?: Function): Promise<void> {
const value = this._serializeObject(val);
return promiseOrCallback(
this._database._native.update(this.path, value),
getNativeModule(this._database).update(this.path, value),
onComplete,
);
}
@ -166,9 +178,9 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @return {Promise}
*/
remove(onComplete?: Function): Promise {
remove(onComplete?: Function): Promise<void> {
return promiseOrCallback(
this._database._native.remove(this.path),
getNativeModule(this._database).remove(this.path),
onComplete,
);
}
@ -187,16 +199,17 @@ export default class Reference extends ReferenceBase {
applyLocally: boolean = false,
) {
if (!isFunction(transactionUpdate)) {
return Promise.reject(
new Error('Missing transactionUpdate function argument.'),
);
return Promise.reject(new Error('Missing transactionUpdate function argument.'));
}
return new Promise((resolve, reject) => {
const onCompleteWrapper = (error, committed, snapshotData) => {
if (isFunction(onComplete)) {
if (error) return onComplete(error, committed, null);
return onComplete(null, committed, new Snapshot(this, snapshotData));
if (error) {
onComplete(error, committed, null);
} else {
onComplete(null, committed, new Snapshot(this, snapshotData));
}
}
if (error) return reject(error);
@ -223,7 +236,7 @@ export default class Reference extends ReferenceBase {
cancelOrContext: (error: FirebaseError) => void,
context?: Object,
) {
return this._database._native.once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
return getNativeModule(this._database).once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
.then(({ snapshot }) => {
const _snapshot = new Snapshot(this, snapshot);
@ -247,7 +260,7 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {*}
*/
push(value: any, onComplete?: Function): Reference | Promise {
push(value: any, onComplete?: Function): Reference | Promise<void> {
if (value === null || value === undefined) {
return new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
}
@ -258,7 +271,9 @@ export default class Reference extends ReferenceBase {
// if callback provided then internally call the set promise with value
if (isFunction(onComplete)) {
return promise
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.catch(error => onComplete(error, null));
}
@ -480,7 +495,7 @@ export default class Reference extends ReferenceBase {
* Access then method of promise if set
* @return {*}
*/
then(fnResolve, fnReject) {
then(fnResolve: (any) => any, fnReject: (any) => any) {
if (isFunction(fnResolve) && this._promise && this._promise.then) {
return this._promise.then.bind(this._promise)((result) => {
this._promise = null;
@ -503,7 +518,7 @@ export default class Reference extends ReferenceBase {
* Access catch method of promise if set
* @return {*}
*/
catch(fnReject) {
catch(fnReject: (any) => any) {
if (isFunction(fnReject) && this._promise && this._promise.catch) {
return this._promise.catch.bind(this._promise)((possibleErr) => {
this._promise = null;
@ -523,8 +538,8 @@ export default class Reference extends ReferenceBase {
*
* @return {string}
*/
_getRegistrationKey(eventType) {
return `$${this._database._appName}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
_getRegistrationKey(eventType: string): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
}
/**
@ -534,15 +549,8 @@ export default class Reference extends ReferenceBase {
* @return {string}
* @private
*/
_getRefKey() {
return `$${this._database._appName}$/${this.path}$${this._query.queryIdentifier()}`;
}
/**
* Return instance of db logger
*/
get log() {
return this._database.log;
_getRefKey(): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}`;
}
/**
@ -550,7 +558,7 @@ export default class Reference extends ReferenceBase {
* @param promise
* @private
*/
_setThenable(promise) {
_setThenable(promise: Promise<*>) {
this._promise = promise;
}
@ -615,7 +623,7 @@ export default class Reference extends ReferenceBase {
*
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on}
*/
on(eventType: string, callback: () => any, cancelCallbackOrContext?: () => any, context?: Object): Function {
on(eventType: string, callback: (Snapshot) => any, cancelCallbackOrContext?: (Object) => any | Object, context?: Object): Function {
if (!eventType) {
throw new Error('Query.on failed: Function called with 0 arguments. Expects at least 2.');
}
@ -648,36 +656,37 @@ export default class Reference extends ReferenceBase {
ref: this,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
appName: this._database.app.name,
eventRegistrationKey,
};
this._syncTree.addRegistration(registrationObj, _context ? callback.bind(_context) : callback);
SyncTree.addRegistration({
...registrationObj,
listener: _context ? callback.bind(_context) : callback,
});
if (isFunction(cancelCallbackOrContext)) {
if (cancelCallbackOrContext && isFunction(cancelCallbackOrContext)) {
// cancellations have their own separate registration
// as these are one off events, and they're not guaranteed
// to occur either, only happens on failure to register on native
this._syncTree.addRegistration(
{
ref: this,
once: true,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
},
_context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
);
SyncTree.addRegistration({
ref: this,
once: true,
path: this.path,
key: this._getRefKey(),
appName: this._database.app.name,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
listener: _context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
});
}
// initialise the native listener if not already listening
this._database._native.on({
getNativeModule(this._database).on({
eventType,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
appName: this._database.app.name,
modifiers: this._query.getModifiers(),
hasCancellationCallback: isFunction(cancelCallbackOrContext),
registration: {
@ -715,7 +724,7 @@ export default class Reference extends ReferenceBase {
if (!arguments.length) {
// Firebase Docs:
// if no eventType or callback is specified, all callbacks for the Reference will be removed.
return this._syncTree.removeListenersForRegistrations(this._syncTree.getRegistrationsByPath(this.path));
return SyncTree.removeListenersForRegistrations(SyncTree.getRegistrationsByPath(this.path));
}
/*
@ -736,29 +745,25 @@ export default class Reference extends ReferenceBase {
// remove the callback.
// Remove only a single registration
if (eventType && originalCallback) {
const registration = this._syncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
const registration = SyncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
if (!registration) return [];
// remove the paired cancellation registration if any exist
this._syncTree.removeListenersForRegistrations([`${registration}$cancelled`]);
SyncTree.removeListenersForRegistrations([`${registration}$cancelled`]);
// remove only the first registration to match firebase web sdk
// call multiple times to remove multiple registrations
return this._syncTree.removeListenerRegistrations(originalCallback, [registration]);
return SyncTree.removeListenerRegistrations(originalCallback, [registration]);
}
// Firebase Docs:
// If a callback is not specified, all callbacks for the specified eventType will be removed.
const registrations = this._syncTree.getRegistrationsByPathEvent(this.path, eventType);
const registrations = SyncTree.getRegistrationsByPathEvent(this.path, eventType);
this._syncTree.removeListenersForRegistrations(
this._syncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
SyncTree.removeListenersForRegistrations(
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
);
return this._syncTree.removeListenersForRegistrations(registrations);
}
get _syncTree() {
return INTERNALS.SyncTree;
return SyncTree.removeListenersForRegistrations(registrations);
}
}

View File

@ -1,15 +1,16 @@
/**
* @flow
* Snapshot representation wrapper
*/
import Reference from './reference.js';
import { isObject, deepGet, deepExists } from './../../utils';
import type Reference from './reference.js';
/**
* @class DataSnapshot
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot
*/
export default class Snapshot {
ref: Object;
ref: Reference;
key: string;
_value: any;
@ -138,7 +139,7 @@ export default class Snapshot {
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#toJSON
* @returns {any}
*/
toJSON(): any {
toJSON(): Object {
return this.val();
}
}

View File

@ -2,19 +2,36 @@
* @flow
* Database Transaction representation wrapper
*/
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import type Database from './';
let transactionId = 0;
/**
* Uses the push id generator to create a transaction id
* @returns {number}
* @private
*/
const generateTransactionId = (): number => {
return transactionId++;
};
/**
* @class TransactionHandler
*/
export default class TransactionHandler {
constructor(database: Object) {
_database: Database;
_transactionListener: Function;
_transactions: { [number]: Object }
constructor(database: Database) {
this._transactions = {};
this._database = database;
this._transactionListener = this._database.addListener(
this._database._getAppEventName('database_transaction_event'),
this._transactionListener = SharedEventEmitter.addListener(
getAppEventName(this._database, 'database_transaction_event'),
this._handleTransactionEvent.bind(this),
);
}
@ -27,7 +44,7 @@ export default class TransactionHandler {
* @param applyLocally
*/
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) {
const id = this._generateTransactionId();
const id = generateTransactionId();
this._transactions[id] = {
id,
@ -39,22 +56,13 @@ export default class TransactionHandler {
started: true,
};
this._database._native.transactionStart(reference.path, id, applyLocally);
getNativeModule(this._database).transactionStart(reference.path, id, applyLocally);
}
/**
* INTERNALS
*/
/**
* Uses the push id generator to create a transaction id
* @returns {string}
* @private
*/
_generateTransactionId(): string {
return transactionId++;
}
/**
*
* @param event
@ -70,7 +78,7 @@ export default class TransactionHandler {
case 'complete':
return this._handleComplete(event);
default:
this.log.warn(`Unknown transaction event type: '${event.type}'`, event);
getLogger(this._database).warn(`Unknown transaction event type: '${event.type}'`, event);
return undefined;
}
}
@ -96,7 +104,7 @@ export default class TransactionHandler {
abort = true;
}
this._database._native.transactionTryCommit(id, { value: newValue, abort });
getNativeModule(this._database).transactionTryCommit(id, { value: newValue, abort });
}
}
@ -110,7 +118,7 @@ export default class TransactionHandler {
if (transaction && !transaction.completed) {
transaction.completed = true;
try {
transaction.onComplete(new Error(event.error.message, event.error.code), null);
transaction.onComplete(event.error, false, null);
} finally {
setImmediate(() => {
delete this._transactions[event.id];

View File

@ -0,0 +1,82 @@
/**
* @flow
* Crash Reporting representation wrapper
*/
import ModuleBase from '../../../utils/ModuleBase';
import { getNativeModule } from '../../../utils/native';
import type App from '../../core/firebase-app';
export const MODULE_NAME = 'RNFirebaseCrashlytics';
export const NAMESPACE = 'crashlytics';
export default class Crashlytics extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
/**
* Forces a crash. Useful for testing your application is set up correctly.
*/
crash(): void {
getNativeModule(this).crash();
}
/**
* Logs a message that will appear in any subsequent crash reports.
* @param {string} message
*/
log(message: string): void {
getNativeModule(this).log(message);
}
/**
* Logs a non fatal exception.
* @param {string} code
* @param {string} message
*/
recordError(code: number, message: string): void {
getNativeModule(this).recordError(code, message);
}
/**
* Set a boolean value to show alongside any subsequent crash reports.
*/
setBoolValue(key: string, value: boolean): void {
getNativeModule(this).setBoolValue(key, value);
}
/**
* Set a float value to show alongside any subsequent crash reports.
*/
setFloatValue(key: string, value: number): void {
getNativeModule(this).setFloatValue(key, value);
}
/**
* Set an integer value to show alongside any subsequent crash reports.
*/
setIntValue(key: string, value: number): void {
getNativeModule(this).setIntValue(key, value);
}
/**
* Set a string value to show alongside any subsequent crash reports.
*/
setStringValue(key: string, value: string): void {
getNativeModule(this).setStringValue(key, value);
}
/**
* Set the user ID to show alongside any subsequent crash reports.
*/
setUserIdentifier(userId: string): void {
getNativeModule(this).setUserIdentifier(userId);
}
}
export const statics = {};

View File

@ -3,28 +3,30 @@
* CollectionReference representation wrapper
*/
import DocumentReference from './DocumentReference';
import Path from './Path';
import Query from './Query';
import QuerySnapshot from './QuerySnapshot';
import { firestoreAutoId } from '../../utils';
import type { Direction, Operator } from './Query';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type Path from './Path';
import type { Observer, ObserverOnError, ObserverOnNext, QueryListenOptions } from './Query';
import type QuerySnapshot from './QuerySnapshot';
/**
/**
* @class CollectionReference
*/
export default class CollectionReference {
_collectionPath: Path;
_firestore: Object;
_firestore: Firestore;
_query: Query;
constructor(firestore: Object, collectionPath: Path) {
constructor(firestore: Firestore, collectionPath: Path) {
this._collectionPath = collectionPath;
this._firestore = firestore;
this._query = new Query(firestore, collectionPath);
}
get firestore(): Object {
get firestore(): Firestore {
return this._firestore;
}
@ -55,47 +57,43 @@ export default class CollectionReference {
}
// From Query
endAt(fieldValues: any): Query {
return this._query.endAt(fieldValues);
endAt(...snapshotOrVarArgs: any[]): Query {
return this._query.endAt(snapshotOrVarArgs);
}
endBefore(fieldValues: any): Query {
return this._query.endBefore(fieldValues);
endBefore(...snapshotOrVarArgs: any[]): Query {
return this._query.endBefore(snapshotOrVarArgs);
}
get(): Promise<QuerySnapshot> {
return this._query.get();
}
limit(n: number): Query {
return this._query.limit(n);
limit(limit: number): Query {
return this._query.limit(limit);
}
offset(n: number): Query {
return this._query.offset(n);
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
): () => void {
return this._query.onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError);
}
onSnapshot(onNext: () => any, onError?: () => any): () => void {
return this._query.onSnapshot(onNext, onError);
}
orderBy(fieldPath: string, directionStr?: Direction): Query {
orderBy(fieldPath: string, directionStr?: FirestoreQueryDirection): Query {
return this._query.orderBy(fieldPath, directionStr);
}
startAfter(fieldValues: any): Query {
return this._query.startAfter(fieldValues);
startAfter(...snapshotOrVarArgs: any[]): Query {
return this._query.startAfter(snapshotOrVarArgs);
}
startAt(fieldValues: any): Query {
return this._query.startAt(fieldValues);
startAt(...snapshotOrVarArgs: any[]): Query {
return this._query.startAt(snapshotOrVarArgs);
}
stream(): Stream<DocumentSnapshot> {
return this._query.stream();
}
where(fieldPath: string, opStr: Operator, value: any): Query {
where(fieldPath: string, opStr: FirestoreQueryOperator, value: any): Query {
return this._query.where(fieldPath, opStr, value);
}
}

View File

@ -4,15 +4,10 @@
*/
import DocumentSnapshot from './DocumentSnapshot';
import type Firestore from './';
import type { FirestoreNativeDocumentChange } from '../../types';
export type DocumentChangeNativeData = {
document: DocumentSnapshot,
newIndex: number,
oldIndex: number,
type: string,
}
/**
/**
* @class DocumentChange
*/
export default class DocumentChange {
@ -21,7 +16,7 @@ export default class DocumentChange {
_oldIndex: number;
_type: string;
constructor(firestore: Object, nativeData: DocumentChangeNativeData) {
constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentChange) {
this._document = new DocumentSnapshot(firestore, nativeData.document);
this._newIndex = nativeData.newIndex;
this._oldIndex = nativeData.oldIndex;

View File

@ -4,36 +4,41 @@
*/
import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import { buildNativeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
export type WriteOptions = {
merge?: boolean,
}
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreWriteOptions } from '../../types';
import type Path from './Path';
type DocumentListenOptions = {
includeMetadataChanges: boolean,
}
type ObserverOnError = (Object) => void;
type ObserverOnNext = (DocumentSnapshot) => void;
type Observer = {
next: (DocumentSnapshot) => void,
error?: (Object) => void,
error?: ObserverOnError,
next: ObserverOnNext,
}
/**
/**
* @class DocumentReference
*/
export default class DocumentReference {
_documentPath: Path;
_firestore: Object;
_firestore: Firestore;
constructor(firestore: Object, documentPath: Path) {
constructor(firestore: Firestore, documentPath: Path) {
this._documentPath = documentPath;
this._firestore = firestore;
}
get firestore(): Object {
get firestore(): Firestore {
return this._firestore;
}
@ -43,6 +48,9 @@ export default class DocumentReference {
get parent(): CollectionReference {
const parentPath = this._documentPath.parent();
if (!parentPath) {
throw new Error('Invalid document path');
}
return new CollectionReference(this._firestore, parentPath);
}
@ -60,30 +68,33 @@ export default class DocumentReference {
}
delete(): Promise<void> {
return this._firestore._native
return getNativeModule(this._firestore)
.documentDelete(this.path);
}
get(): Promise<DocumentSnapshot> {
return this._firestore._native
return getNativeModule(this._firestore)
.documentGet(this.path)
.then(result => new DocumentSnapshot(this._firestore, result));
}
onSnapshot(
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
) {
let observer = {};
let observer: Observer;
let docListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
@ -91,7 +102,11 @@ export default class DocumentReference {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext.next,
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
@ -99,18 +114,24 @@ export default class DocumentReference {
docListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('DocumentReference.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
@ -125,40 +146,40 @@ export default class DocumentReference {
}
const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
const listener = (nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this.firestore, nativeDocumentSnapshot);
observer.next(documentSnapshot);
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
observer.error,
);
}
// Add the native listener
this._firestore._native
getNativeModule(this._firestore)
.documentOnSnapshot(this.path, listenerId, docListenOptions);
// Return an unsubscribe method
return this._offDocumentSnapshot.bind(this, listenerId, listener);
}
set(data: Object, writeOptions?: WriteOptions): Promise<void> {
set(data: Object, writeOptions?: FirestoreWriteOptions): Promise<void> {
const nativeData = buildNativeMap(data);
return this._firestore._native
return getNativeModule(this._firestore)
.documentSet(this.path, nativeData, writeOptions);
}
update(...args: Object | string[]): Promise<void> {
update(...args: any[]): Promise<void> {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
@ -178,7 +199,7 @@ export default class DocumentReference {
}
}
const nativeData = buildNativeMap(data);
return this._firestore._native
return getNativeModule(this._firestore)
.documentUpdate(this.path, nativeData);
}
@ -190,11 +211,11 @@ export default class DocumentReference {
* Remove document snapshot listener
* @param listener
*/
_offDocumentSnapshot(listenerId: number, listener: Function) {
this._firestore.log.info('Removing onDocumentSnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener);
this._firestore._native
_offDocumentSnapshot(listenerId: string, listener: Function) {
getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.documentOffSnapshot(this.path, listenerId);
}
}

View File

@ -6,26 +6,18 @@ import DocumentReference from './DocumentReference';
import Path from './Path';
import { parseNativeMap } from './utils/serialize';
export type SnapshotMetadata = {
fromCache: boolean,
hasPendingWrites: boolean,
}
export type DocumentSnapshotNativeData = {
data: Object,
metadata: SnapshotMetadata,
path: string,
}
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
/**
* @class DocumentSnapshot
*/
export default class DocumentSnapshot {
_data: Object;
_metadata: SnapshotMetadata;
_data: Object | void;
_metadata: FirestoreSnapshotMetadata;
_ref: DocumentReference;
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentSnapshot) {
this._data = parseNativeMap(firestore, nativeData.data);
this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
@ -39,7 +31,7 @@ export default class DocumentSnapshot {
return this._ref.id;
}
get metadata(): SnapshotMetadata {
get metadata(): FirestoreSnapshotMetadata {
return this._metadata;
}
@ -47,11 +39,11 @@ export default class DocumentSnapshot {
return this._ref;
}
data(): Object {
data(): Object | void {
return this._data;
}
get(fieldPath: string): any {
return this._data[fieldPath];
return this._data ? this._data[fieldPath] : undefined;
}
}

View File

@ -3,7 +3,7 @@
* GeoPoint representation wrapper
*/
/**
/**
* @class GeoPoint
*/
export default class GeoPoint {
@ -19,11 +19,11 @@ export default class GeoPoint {
this._longitude = longitude;
}
get latitude() {
get latitude(): number {
return this._latitude;
}
get longitude() {
get longitude(): number {
return this._longitude;
}
}

View File

@ -3,7 +3,7 @@
* Path representation wrapper
*/
/**
/**
* @class Path
*/
export default class Path {

View File

@ -3,19 +3,25 @@
* Query representation wrapper
*/
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import QuerySnapshot from './QuerySnapshot';
import { buildNativeArray, buildTypeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
const DIRECTIONS = {
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type Path from './Path';
const DIRECTIONS: { [FirestoreQueryDirection]: string } = {
ASC: 'ASCENDING',
asc: 'ASCENDING',
DESC: 'DESCENDING',
desc: 'DESCENDING',
};
const OPERATORS = {
const OPERATORS: { [FirestoreQueryOperator]: string } = {
'=': 'EQUAL',
'==': 'EQUAL',
'>': 'GREATER_THAN',
@ -24,7 +30,6 @@ const OPERATORS = {
'<=': 'LESS_THAN_OR_EQUAL',
};
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
type FieldFilter = {
fieldPath: string,
operator: string,
@ -43,30 +48,39 @@ type QueryOptions = {
startAfter?: any[],
startAt?: any[],
}
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
type QueryListenOptions = {
includeQueryMetadataChanges: boolean,
export type QueryListenOptions = {
includeDocumentMetadataChanges: boolean,
includeQueryMetadataChanges: boolean,
}
type Observer = {
next: (DocumentSnapshot) => void,
error?: (Object) => void,
export type ObserverOnError = (Object) => void;
export type ObserverOnNext = (QuerySnapshot) => void;
export type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
}
/**
/**
* @class Query
*/
export default class Query {
_fieldFilters: FieldFilter[];
_fieldOrders: FieldOrder[];
_firestore: Object;
_firestore: Firestore;
_iid: number;
_queryOptions: QueryOptions;
_referencePath: Path;
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[],
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) {
constructor(
firestore: Firestore,
path: Path,
fieldFilters?:
FieldFilter[],
fieldOrders?: FieldOrder[],
queryOptions?: QueryOptions,
) {
this._fieldFilters = fieldFilters || [];
this._fieldOrders = fieldOrders || [];
this._firestore = firestore;
@ -74,32 +88,42 @@ export default class Query {
this._referencePath = path;
}
get firestore(): Object {
get firestore(): Firestore {
return this._firestore;
}
endAt(...snapshotOrVarArgs: any): Query {
endAt(...snapshotOrVarArgs: any[]): Query {
const options = {
...this._queryOptions,
endAt: this._buildOrderByOption(snapshotOrVarArgs),
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
}
endBefore(...snapshotOrVarArgs: any): Query {
endBefore(...snapshotOrVarArgs: any[]): Query {
const options = {
...this._queryOptions,
endBefore: this._buildOrderByOption(snapshotOrVarArgs),
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
}
get(): Promise<QuerySnapshot> {
return this._firestore._native
return getNativeModule(this._firestore)
.collectionGet(
this._referencePath.relativeName,
this._fieldFilters,
@ -117,24 +141,32 @@ export default class Query {
...this._queryOptions,
limit,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
}
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void,
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
) {
let observer = {};
let observer: Observer;
let queryListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('Query.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
@ -142,7 +174,11 @@ export default class Query {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext.next,
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
@ -150,18 +186,24 @@ export default class Query {
queryListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('Query.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
@ -174,7 +216,6 @@ export default class Query {
} else {
throw new Error('Query.onSnapshot failed: Called with invalid arguments.');
}
const listenerId = firestoreAutoId();
const listener = (nativeQuerySnapshot) => {
@ -183,35 +224,35 @@ export default class Query {
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
observer.error,
);
}
// Add the native listener
this._firestore._native
getNativeModule(this._firestore)
.collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
queryListenOptions,
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
queryListenOptions,
);
// Return an unsubscribe method
return this._offCollectionSnapshot.bind(this, listenerId, listener);
}
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
orderBy(fieldPath: string, directionStr?: FirestoreQueryDirection = 'asc'): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isOptionalFieldOrder('directionStr', directionStr);
@ -226,31 +267,46 @@ export default class Query {
fieldPath,
};
const combinedOrders = this._fieldOrders.concat(newOrder);
return new Query(this.firestore, this._referencePath, this._fieldFilters,
combinedOrders, this._queryOptions);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
combinedOrders,
this._queryOptions,
);
}
startAfter(...snapshotOrVarArgs: any): Query {
startAfter(...snapshotOrVarArgs: any[]): Query {
const options = {
...this._queryOptions,
startAfter: this._buildOrderByOption(snapshotOrVarArgs),
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
}
startAt(...snapshotOrVarArgs: any): Query {
startAt(...snapshotOrVarArgs: any[]): Query {
const options = {
...this._queryOptions,
startAt: this._buildOrderByOption(snapshotOrVarArgs),
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
return new Query(
this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
}
where(fieldPath: string, opStr: Operator, value: any): Query {
where(fieldPath: string, opStr: FirestoreQueryOperator, value: any): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isFieldFilter('fieldFilter', opStr, value);
@ -261,8 +317,13 @@ export default class Query {
value: nativeValue,
};
const combinedFilters = this._fieldFilters.concat(newFilter);
return new Query(this.firestore, this._referencePath, combinedFilters,
this._fieldOrders, this._queryOptions);
return new Query(
this.firestore,
this._referencePath,
combinedFilters,
this._fieldOrders,
this._queryOptions,
);
}
/**
@ -290,17 +351,17 @@ export default class Query {
* Remove query snapshot listener
* @param listener
*/
_offCollectionSnapshot(listenerId: number, listener: Function) {
this._firestore.log.info('Removing onQuerySnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
this._firestore._native
_offCollectionSnapshot(listenerId: string, listener: Function) {
getLogger(this._firestore).info('Removing onQuerySnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.collectionOffSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId
listenerId,
);
}
}

View File

@ -4,27 +4,27 @@
*/
import DocumentChange from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot';
import Query from './Query';
import type { DocumentChangeNativeData } from './DocumentChange';
import type { DocumentSnapshotNativeData, SnapshotMetadata } from './DocumentSnapshot';
import type Firestore from './';
import type { FirestoreNativeDocumentChange, FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
import type Query from './Query';
type QuerySnapshotNativeData = {
changes: DocumentChangeNativeData[],
documents: DocumentSnapshotNativeData[],
metadata: SnapshotMetadata,
changes: FirestoreNativeDocumentChange[],
documents: FirestoreNativeDocumentSnapshot[],
metadata: FirestoreSnapshotMetadata,
}
/**
/**
* @class QuerySnapshot
*/
export default class QuerySnapshot {
_changes: DocumentChange[];
_docs: DocumentSnapshot[];
_metadata: SnapshotMetadata;
_metadata: FirestoreSnapshotMetadata;
_query: Query;
constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) {
constructor(firestore: Firestore, query: Query, nativeData: QuerySnapshotNativeData) {
this._changes = nativeData.changes.map(change => new DocumentChange(firestore, change));
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
this._metadata = nativeData.metadata;
@ -43,12 +43,12 @@ export default class QuerySnapshot {
return this._docs.length === 0;
}
get query(): Query {
return this._query;
get metadata(): FirestoreSnapshotMetadata {
return this._metadata;
}
get metadata(): SnapshotMetadata {
return this._metadata;
get query(): Query {
return this._query;
}
get size(): number {
@ -59,8 +59,8 @@ export default class QuerySnapshot {
// TODO: Validation
// validate.isFunction('callback', callback);
for (const doc of this._docs) {
this._docs.forEach((doc) => {
callback(doc);
}
});
}
}

View File

@ -2,11 +2,13 @@
* @flow
* WriteBatch representation wrapper
*/
import DocumentReference from './DocumentReference';
import { buildNativeMap } from './utils/serialize';
import { isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type { WriteOptions } from './DocumentReference';
import type DocumentReference from './DocumentReference';
import type Firestore from './';
import type { FirestoreWriteOptions } from '../../types';
type DocumentWrite = {
data?: Object,
@ -15,21 +17,20 @@ type DocumentWrite = {
type: 'DELETE' | 'SET' | 'UPDATE',
}
/**
/**
* @class WriteBatch
*/
export default class WriteBatch {
_firestore: Object;
_firestore: Firestore;
_writes: DocumentWrite[];
constructor(firestore: Object) {
constructor(firestore: Firestore) {
this._firestore = firestore;
this._writes = [];
}
commit(): Promise<void> {
return this._firestore._native
.documentBatch(this._writes);
return getNativeModule(this._firestore).documentBatch(this._writes);
}
delete(docRef: DocumentReference): WriteBatch {
@ -44,7 +45,7 @@ export default class WriteBatch {
return this;
}
set(docRef: DocumentReference, data: Object, writeOptions?: WriteOptions) {
set(docRef: DocumentReference, data: Object, writeOptions?: FirestoreWriteOptions) {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data);
@ -60,7 +61,7 @@ export default class WriteBatch {
return this;
}
update(docRef: DocumentReference, ...args: Object | string[]): WriteBatch {
update(docRef: DocumentReference, ...args: any[]): WriteBatch {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
let data = {};

View File

@ -2,15 +2,21 @@
* @flow
* Firestore representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import { NativeModules } from 'react-native';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference';
import DocumentSnapshot from './DocumentSnapshot';
import FieldValue from './FieldValue';
import GeoPoint from './GeoPoint';
import Path from './Path';
import WriteBatch from './WriteBatch';
import INTERNALS from './../../internals';
import INTERNALS from '../../utils/internals';
import type DocumentSnapshot from './DocumentSnapshot';
import type App from '../core/firebase-app';
import type QuerySnapshot from './QuerySnapshot';
type CollectionSyncEvent = {
appName: string,
@ -28,30 +34,40 @@ type DocumentSyncEvent = {
path: string,
}
const NATIVE_EVENTS = [
'firestore_collection_sync_event',
'firestore_document_sync_event',
];
export const MODULE_NAME = 'RNFirebaseFirestore';
export const NAMESPACE = 'firestore';
/**
* @class Firestore
*/
export default class Firestore extends ModuleBase {
static _NAMESPACE = 'firestore';
static _NATIVE_MODULE = 'RNFirebaseFirestore';
_referencePath: Path;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._referencePath = new Path([]);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
this._getAppEventName('firestore_collection_sync_event'),
getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onDocumentSnapshot
this._getAppEventName('firestore_document_sync_event'),
getAppEventName(this, 'firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this),
);
}
@ -92,22 +108,18 @@ export default class Firestore extends ModuleBase {
throw new Error('Persistence is enabled by default on the Firestore SDKs');
}
runTransaction(updateFunction): Promise<any> {
runTransaction(): Promise<any> {
throw new Error('firebase.firestore().runTransaction() coming soon');
}
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel'));
setLogLevel(): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('firestore', 'setLogLevel'));
}
settings(settings: Object): void {
settings(): void {
throw new Error('firebase.firestore().settings() coming soon');
}
/**
* INTERNALS
*/
/**
* Internal collection sync listener
* @param event
@ -115,9 +127,9 @@ export default class Firestore extends ModuleBase {
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
this.emit(this._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(this._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
}
}
@ -128,9 +140,9 @@ export default class Firestore extends ModuleBase {
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
this.emit(this._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(this._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
}
}
}
@ -138,4 +150,9 @@ export default class Firestore extends ModuleBase {
export const statics = {
FieldValue,
GeoPoint,
enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);
}
},
};

View File

@ -1,4 +1,6 @@
// @flow
/**
* @flow
*/
import DocumentReference from '../DocumentReference';
import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
@ -6,10 +8,8 @@ import GeoPoint from '../GeoPoint';
import Path from '../Path';
import { typeOf } from '../../../utils';
type TypeMap = {
type: 'array' | 'boolean' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
value: any,
}
import type Firestore from '../';
import type { FirestoreTypeMap } from '../../../types';
/*
* Functions that build up the data needed to represent
@ -17,65 +17,86 @@ type TypeMap = {
* for transmission to the native side
*/
export const buildNativeMap = (data: Object): Object => {
export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } => {
const nativeData = {};
if (data) {
Object.keys(data).forEach((key) => {
nativeData[key] = buildTypeMap(data[key]);
const typeMap = buildTypeMap(data[key]);
if (typeMap) {
nativeData[key] = typeMap;
}
});
}
return nativeData;
};
export const buildNativeArray = (array: Object[]): any[] => {
export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => {
const nativeArray = [];
if (array) {
array.forEach((value) => {
nativeArray.push(buildTypeMap(value));
const typeMap = buildTypeMap(value);
if (typeMap) {
nativeArray.push(typeMap);
}
});
}
return nativeArray;
};
export const buildTypeMap = (value: any): any => {
const typeMap = {};
export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
const type = typeOf(value);
if (value === null || value === undefined) {
typeMap.type = 'null';
typeMap.value = null;
return {
type: 'null',
value: null,
};
} else if (value === DELETE_FIELD_VALUE) {
typeMap.type = 'fieldvalue';
typeMap.value = 'delete';
return {
type: 'fieldvalue',
value: 'delete',
};
} else if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
typeMap.type = 'fieldvalue';
typeMap.value = 'timestamp';
return {
type: 'fieldvalue',
value: 'timestamp',
};
} else if (type === 'boolean' || type === 'number' || type === 'string') {
typeMap.type = type;
typeMap.value = value;
return {
type,
value,
};
} else if (type === 'array') {
typeMap.type = type;
typeMap.value = buildNativeArray(value);
return {
type,
value: buildNativeArray(value),
};
} else if (type === 'object') {
if (value instanceof DocumentReference) {
typeMap.type = 'reference';
typeMap.value = value.path;
return {
type: 'reference',
value: value.path,
};
} else if (value instanceof GeoPoint) {
typeMap.type = 'geopoint';
typeMap.value = {
latitude: value.latitude,
longitude: value.longitude,
return {
type: 'geopoint',
value: {
latitude: value.latitude,
longitude: value.longitude,
},
};
} else if (value instanceof Date) {
typeMap.type = 'date';
typeMap.value = value.getTime();
} else {
typeMap.type = 'object';
typeMap.value = buildNativeMap(value);
return {
type: 'date',
value: value.getTime(),
};
}
} else {
console.warn(`Unknown data type received ${type}`);
return {
type: 'object',
value: buildNativeMap(value),
};
}
return typeMap;
console.warn(`Unknown data type received ${type}`);
return null;
};
/*
@ -83,7 +104,7 @@ export const buildTypeMap = (value: any): any => {
* side and converts to the correct Firestore JS types
*/
export const parseNativeMap = (firestore: Object, nativeData: Object): Object => {
export const parseNativeMap = (firestore: Firestore, nativeData: { [string]: FirestoreTypeMap }): Object | void => {
let data;
if (nativeData) {
data = {};
@ -94,7 +115,7 @@ export const parseNativeMap = (firestore: Object, nativeData: Object): Object =>
return data;
};
const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => {
const parseNativeArray = (firestore: Firestore, nativeArray: FirestoreTypeMap[]): any[] => {
const array = [];
if (nativeArray) {
nativeArray.forEach((typeMap) => {
@ -104,7 +125,7 @@ const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => {
return array;
};
const parseTypeMap = (firestore: Object, typeMap: TypeMap): any => {
const parseTypeMap = (firestore: Firestore, typeMap: FirestoreTypeMap): any => {
const { type, value } = typeMap;
if (type === 'null') {
return null;

View File

@ -1,10 +1,25 @@
import ModuleBase from './../../utils/ModuleBase';
import { areObjectKeysContainedInOther, isObject, isString } from './../../utils';
/**
* @flow
* Dynamic Links representation wrapper
*/
import { SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import { areObjectKeysContainedInOther, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
const EVENT_TYPE = {
Link: 'dynamic_link_received',
};
const NATIVE_EVENTS = [
EVENT_TYPE.Link,
];
export const MODULE_NAME = 'RNFirebaseLinks';
export const NAMESPACE = 'links';
function validateParameters(parameters: Object): void {
const suportedParametersObject = {
dynamicLinkDomain: 'string',
@ -56,14 +71,16 @@ function checkForMandatoryParameters(parameters: Object): void {
* @class Links
*/
export default class Links extends ModuleBase {
static _NAMESPACE = 'links';
static _NATIVE_MODULE = 'RNFirebaseLinks';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
get EVENT_TYPE() {
get EVENT_TYPE(): Object {
return EVENT_TYPE;
}
@ -71,8 +88,8 @@ export default class Links extends ModuleBase {
* Returns the link that triggered application open
* @returns {Promise.<String>}
*/
getInitialLink() {
return this._native.getInitialLink();
getInitialLink(): Promise<string> {
return getNativeModule(this).getInitialLink();
}
/**
@ -81,7 +98,7 @@ export default class Links extends ModuleBase {
* @returns {Function}
*/
onLink(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.Link, listener);
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.Link, listener);
return () => rnListener.remove();
}
@ -90,11 +107,11 @@ export default class Links extends ModuleBase {
* @param parameters
* @returns {Promise.<String>}
*/
createDynamicLink(parameters: Object = {}): Promise<String> {
createDynamicLink(parameters: Object = {}): Promise<string> {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createDynamicLink(parameters);
return getNativeModule(this).createDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}
@ -109,7 +126,7 @@ export default class Links extends ModuleBase {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createShortDynamicLink(parameters);
return getNativeModule(this).createShortDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}

View File

@ -1,7 +1,13 @@
/**
* @flow
* Remote message representation wrapper
*/
import { isObject, generatePushID } from './../../utils';
export default class RemoteMessage {
constructor(sender: String) {
properties: Object;
constructor(sender: string) {
this.properties = {
id: generatePushID(),
ttl: 3600,
@ -17,7 +23,7 @@ export default class RemoteMessage {
* @param ttl
* @returns {RemoteMessage}
*/
setTtl(ttl: Number): RemoteMessage {
setTtl(ttl: number): RemoteMessage {
this.properties.ttl = ttl;
return this;
}
@ -74,7 +80,7 @@ export default class RemoteMessage {
return this;
}
toJSON() {
toJSON(): Object {
return Object.assign({}, this.properties);
}
}

View File

@ -1,6 +1,14 @@
/**
* @flow
* Messaging representation wrapper
*/
import { Platform, NativeModules } from 'react-native';
import ModuleBase from './../../utils/ModuleBase';
import { SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import RemoteMessage from './RemoteMessage';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
const EVENT_TYPE = {
RefreshToken: 'messaging_token_refreshed',
@ -25,6 +33,11 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const NATIVE_EVENTS = [
EVENT_TYPE.RefreshToken,
EVENT_TYPE.Notification,
];
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
/**
@ -67,31 +80,35 @@ function finish(data) {
}
}
export const MODULE_NAME = 'RNFirebaseMessaging';
export const NAMESPACE = 'messaging';
/**
* @class Messaging
*/
export default class Messaging extends ModuleBase {
static _NAMESPACE = 'messaging';
static _NATIVE_MODULE = 'RNFirebaseMessaging';
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
get EVENT_TYPE() {
get EVENT_TYPE(): Object {
return EVENT_TYPE;
}
get NOTIFICATION_TYPE() {
get NOTIFICATION_TYPE(): Object {
return NOTIFICATION_TYPE;
}
get REMOTE_NOTIFICATION_RESULT() {
get REMOTE_NOTIFICATION_RESULT(): Object {
return REMOTE_NOTIFICATION_RESULT;
}
get WILL_PRESENT_RESULT() {
get WILL_PRESENT_RESULT(): Object {
return WILL_PRESENT_RESULT;
}
@ -99,24 +116,24 @@ export default class Messaging extends ModuleBase {
* Returns the notification that triggered application open
* @returns {*}
*/
getInitialNotification() {
return this._native.getInitialNotification();
getInitialNotification(): Promise<Object> {
return getNativeModule(this).getInitialNotification();
}
/**
* Returns the fcm token for the current device
* @returns {*|Promise.<String>}
*/
getToken() {
return this._native.getToken();
getToken(): Promise<string> {
return getNativeModule(this).getToken();
}
/**
* Reset Instance ID and revokes all tokens.
* @returns {*|Promise.<*>}
*/
deleteInstanceId() {
return this._native.deleteInstanceId();
deleteInstanceId(): Promise<void> {
return getNativeModule(this).deleteInstanceId();
}
/**
@ -124,11 +141,11 @@ export default class Messaging extends ModuleBase {
* @param notification
* @returns {*}
*/
createLocalNotification(notification: Object) {
createLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
_notification.id = _notification.id || new Date().getTime().toString();
_notification.local_notification = true;
return this._native.createLocalNotification(_notification);
return getNativeModule(this).createLocalNotification(_notification);
}
/**
@ -136,19 +153,19 @@ export default class Messaging extends ModuleBase {
* @param notification
* @returns {*}
*/
scheduleLocalNotification(notification: Object) {
scheduleLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
if (!notification.id) return Promise.reject(new Error('An id is required to schedule a local notification.'));
_notification.local_notification = true;
return this._native.scheduleLocalNotification(_notification);
return getNativeModule(this).scheduleLocalNotification(_notification);
}
/**
* Returns an array of all scheduled notifications
* @returns {Promise.<Array>}
*/
getScheduledLocalNotifications() {
return this._native.getScheduledLocalNotifications();
getScheduledLocalNotifications(): Promise<Object[]> {
return getNativeModule(this).getScheduledLocalNotifications();
}
/**
@ -157,10 +174,10 @@ export default class Messaging extends ModuleBase {
* @param id
* @returns {*}
*/
cancelLocalNotification(id: string) {
if (!id) return null;
if (id === '*') return this._native.cancelAllLocalNotifications();
return this._native.cancelLocalNotification(id);
cancelLocalNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return getNativeModule(this).cancelAllLocalNotifications();
return getNativeModule(this).cancelLocalNotification(id);
}
/**
@ -169,10 +186,10 @@ export default class Messaging extends ModuleBase {
* @param id
* @returns {*}
*/
removeDeliveredNotification(id: string) {
if (!id) return null;
if (id === '*') return this._native.removeAllDeliveredNotifications();
return this._native.removeDeliveredNotification(id);
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return getNativeModule(this).removeAllDeliveredNotifications();
return getNativeModule(this).removeDeliveredNotification(id);
}
/**
@ -180,8 +197,8 @@ export default class Messaging extends ModuleBase {
* @platforms ios
* @returns {*|Promise.<*>}
*/
requestPermissions() {
return this._native.requestPermissions();
requestPermissions(): Promise<void> {
return getNativeModule(this).requestPermissions();
}
@ -189,16 +206,16 @@ export default class Messaging extends ModuleBase {
* Set notification count badge number
* @param n
*/
setBadgeNumber(n: number) {
this._native.setBadgeNumber(n);
setBadgeNumber(n: number): void {
getNativeModule(this).setBadgeNumber(n);
}
/**
* set notification count badge number
* @returns {Promise.<Number>}
*/
getBadgeNumber() {
return this._native.getBadgeNumber();
getBadgeNumber(): Promise<number> {
return getNativeModule(this).getBadgeNumber();
}
/**
@ -206,8 +223,8 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onMessage(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(
onMessage(listener: (Object) => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Notification,
async (event) => {
const data = {
@ -229,8 +246,8 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onTokenRefresh(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
onTokenRefresh(listener: (string) => any): () => any {
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
return () => rnListener.remove();
}
@ -238,28 +255,28 @@ export default class Messaging extends ModuleBase {
* Subscribe to a topic
* @param topic
*/
subscribeToTopic(topic: String) {
this._native.subscribeToTopic(topic);
subscribeToTopic(topic: string): void {
getNativeModule(this).subscribeToTopic(topic);
}
/**
* Unsubscribe from a topic
* @param topic
*/
unsubscribeFromTopic(topic: String) {
this._native.unsubscribeFromTopic(topic);
unsubscribeFromTopic(topic: string): void {
getNativeModule(this).unsubscribeFromTopic(topic);
}
/**
* Send an upstream message
* @param remoteMessage
*/
send(remoteMessage: RemoteMessage) {
send(remoteMessage: RemoteMessage): Promise<void> {
if (!(remoteMessage instanceof RemoteMessage)) {
throw new Error('messaging().send requires an instance of RemoteMessage as the first argument.');
}
return this._native.send(remoteMessage.toJSON());
return getNativeModule(this).send(remoteMessage.toJSON());
}
}

View File

@ -1,19 +1,28 @@
export default class Trace {
/**
* @flow
* Trace representation wrapper
*/
import { getNativeModule } from '../../utils/native';
import type PerformanceMonitoring from './';
constructor(perf: Object, identifier: string) {
this.perf = perf;
export default class Trace {
identifier: string;
_perf: PerformanceMonitoring;
constructor(perf: PerformanceMonitoring, identifier: string) {
this._perf = perf;
this.identifier = identifier;
}
start() {
this.perf._native.start(this.identifier);
start(): void {
getNativeModule(this._perf).start(this.identifier);
}
stop() {
this.perf._native.stop(this.identifier);
stop(): void {
getNativeModule(this._perf).stop(this.identifier);
}
incrementCounter(event: string) {
this.perf._native.incrementCounter(this.identifier, event);
incrementCounter(event: string): void {
getNativeModule(this._perf).incrementCounter(this.identifier, event);
}
}

View File

@ -1,15 +1,23 @@
// @flow
/**
* @flow
* Performance monitoring representation wrapper
*/
import Trace from './Trace';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
export const MODULE_NAME = 'RNFirebasePerformance';
export const NAMESPACE = 'perf';
export default class PerformanceMonitoring extends ModuleBase {
static _NAMESPACE = 'perf';
static _NATIVE_MODULE = 'RNFirebasePerformance';
_native: Object;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options);
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
/**
@ -17,15 +25,17 @@ export default class PerformanceMonitoring extends ModuleBase {
* @param enabled
* @returns {*}
*/
setPerformanceCollectionEnabled(enabled: boolean) {
return this._native.setPerformanceCollectionEnabled(enabled);
setPerformanceCollectionEnabled(enabled: boolean): void {
getNativeModule(this).setPerformanceCollectionEnabled(enabled);
}
/**
* Returns a new trace instance
* @param trace
*/
newTrace(trace: string): void {
newTrace(trace: string): Trace {
return new Trace(this, trace);
}
}
export const statics = {};

View File

@ -1,30 +1,48 @@
/* @flow */
/**
* @flow
* Storage representation wrapper
*/
import { NativeModules } from 'react-native';
import StorageRef from './reference';
import ModuleBase from './../../utils/ModuleBase';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
const FirebaseStorage = NativeModules.RNFirebaseStorage;
const NATIVE_EVENTS = [
'storage_event',
'storage_error',
];
export const MODULE_NAME = 'RNFirebaseStorage';
export const NAMESPACE = 'storage';
export default class Storage extends ModuleBase {
static _NAMESPACE = 'storage';
static _NATIVE_MODULE = 'RNFirebaseStorage';
/**
*
* @param firebaseApp
* @param app
* @param options
*/
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
this._subscriptions = {};
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this.addListener(
this._getAppEventName('storage_event'),
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_event'),
this._handleStorageEvent.bind(this),
);
this.addListener(
this._getAppEventName('storage_error'),
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_error'),
this._handleStorageEvent.bind(this),
);
}
@ -45,7 +63,7 @@ export default class Storage extends ModuleBase {
* @param url
* @returns {StorageReference}
*/
refFromURL(url: string): Promise<StorageRef> {
refFromURL(url: string): StorageRef {
// TODO don't think this is correct?
return new StorageRef(this, `url::${url}`);
}
@ -55,8 +73,8 @@ export default class Storage extends ModuleBase {
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
* @param time The new maximum operation retry time in milliseconds.
*/
setMaxOperationRetryTime(time: number) {
this._native.setMaxOperationRetryTime(time);
setMaxOperationRetryTime(time: number): void {
getNativeModule(this).setMaxOperationRetryTime(time);
}
/**
@ -64,8 +82,8 @@ export default class Storage extends ModuleBase {
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
* @param time The new maximum upload retry time in milliseconds.
*/
setMaxUploadRetryTime(time: number) {
this._native.setMaxUploadRetryTime(time);
setMaxUploadRetryTime(time: number): void {
getNativeModule(this).setMaxUploadRetryTime(time);
}
/**
@ -73,39 +91,39 @@ export default class Storage extends ModuleBase {
* @url N/A
* @param time The new maximum download retry time in milliseconds.
*/
setMaxDownloadRetryTime(time: number) {
this._native.setMaxDownloadRetryTime(time);
setMaxDownloadRetryTime(time: number): void {
getNativeModule(this).setMaxDownloadRetryTime(time);
}
/** **********
/**
* INTERNALS
********** **/
_getSubEventName(path, eventName) {
return this._getAppEventName(`${path}-${eventName}`);
*/
_getSubEventName(path: string, eventName: string) {
return getAppEventName(this, `${path}-${eventName}`);
}
_handleStorageEvent(event: Object) {
const { path, eventName } = event;
const body = event.body || {};
this.log.debug('_handleStorageEvent: ', path, eventName, body);
this.emit(this._getSubEventName(path, eventName), body);
getLogger(this).debug('_handleStorageEvent: ', path, eventName, body);
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_handleStorageError(err: Object) {
const { path, eventName } = event;
const body = event.body || {};
const { path, eventName } = err;
const body = err.body || {};
this.log.debug('_handleStorageError ->', err);
this.emit(this._getSubEventName(path, eventName), body);
getLogger(this).debug('_handleStorageError ->', err);
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_addListener(path: string, eventName: string, cb: (evt: Object) => Object) {
this.on(this._getSubEventName(path, eventName), cb);
_addListener(path: string, eventName: string, cb: (evt: Object) => Object): void {
SharedEventEmitter.addListener(this._getSubEventName(path, eventName), cb);
}
_removeListener(path: string, eventName: string, origCB: (evt: Object) => Object) {
this.removeListener(this._getSubEventName(path, eventName), origCB);
_removeListener(path: string, eventName: string, origCB: (evt: Object) => Object): void {
SharedEventEmitter.removeListener(this._getSubEventName(path, eventName), origCB);
}
}

View File

@ -1,16 +1,21 @@
/* @flow */
/**
* @flow
* StorageReference representation wrapper
*/
import ReferenceBase from '../../utils/ReferenceBase';
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
import Storage from './';
import { getNativeModule } from '../../utils/native';
import type Storage from './';
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
*/
export default class StorageReference extends ReferenceBase {
_storage: Storage;
constructor(storage: Storage, path: string) {
super(path, storage);
super(path);
this._storage = storage;
}
@ -18,7 +23,7 @@ export default class StorageReference extends ReferenceBase {
return this.path;
}
toString(): String {
toString(): string {
return `gs://${this._storage.app.options.storageBucket}${this.path}`;
}
@ -27,24 +32,24 @@ export default class StorageReference extends ReferenceBase {
* @param path
* @returns {StorageReference}
*/
child(path: string) {
return new StorageReference(this._module, `${this.path}/${path}`);
child(path: string): StorageReference {
return new StorageReference(this._storage, `${this.path}/${path}`);
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
* @returns {Promise.<T>|*}
*/
delete(): Promise<*> {
return this._module._native.delete(this.path);
delete(): Promise<void> {
return getNativeModule(this._storage).delete(this.path);
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
* @returns {Promise.<T>|*}
*/
getDownloadURL(): Promise<String> {
return this._module._native.getDownloadURL(this.path);
getDownloadURL(): Promise<string> {
return getNativeModule(this._storage).getDownloadURL(this.path);
}
/**
@ -52,7 +57,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
getMetadata(): Promise<Object> {
return this._module._native.getMetadata(this.path);
return getNativeModule(this._storage).getMetadata(this.path);
}
/**
@ -61,7 +66,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
updateMetadata(metadata: Object = {}): Promise<Object> {
return this._module._native.updateMetadata(this.path, metadata);
return getNativeModule(this._storage).updateMetadata(this.path, metadata);
}
/**
@ -70,14 +75,14 @@ export default class StorageReference extends ReferenceBase {
* @return {Promise}
*/
downloadFile(filePath: string): Promise<Object> {
return new StorageTask(DOWNLOAD_TASK, this._module._native.downloadFile(this.path, filePath), this);
return new StorageTask(DOWNLOAD_TASK, getNativeModule(this._storage).downloadFile(this.path, filePath), this);
}
/**
* Alias to putFile
* @returns {StorageReference.putFile}
*/
get put(): Function {
get put(): (Object, Object) => Promise<Object> {
return this.putFile;
}
@ -89,6 +94,6 @@ export default class StorageReference extends ReferenceBase {
*/
putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
const _filePath = filePath.replace('file://', '');
return new StorageTask(UPLOAD_TASK, this._module._native.putFile(this.path, _filePath, metadata), this);
return new StorageTask(UPLOAD_TASK, getNativeModule(this._storage).putFile(this.path, _filePath, metadata), this);
}
}

View File

@ -1,7 +1,11 @@
/* @flow */
/**
* @flow
* UploadTask representation wrapper
*/
import { statics as StorageStatics } from './';
import { isFunction } from './../../utils';
import StorageReference from './reference';
import type Storage from './';
import type StorageReference from './reference';
export const UPLOAD_TASK = 'upload';
export const DOWNLOAD_TASK = 'download';
@ -40,15 +44,15 @@ declare type NextOrObserverType = null |
export default class StorageTask {
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK;
ref: StorageReference;
storage: StorageReference.storage;
path: StorageReference.path;
storage: Storage;
path: string;
then: () => Promise<*>;
catch: () => Promise<*>;
constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) {
this.type = type;
this.ref = storageRef;
this.storage = storageRef._module;
this.storage = storageRef._storage;
this.path = storageRef.path;
// 'proxy' original promise

View File

@ -1,42 +1,53 @@
// @flow
import { NativeModules } from 'react-native';
// import { version as ReactVersion } from 'react';
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion';
import INTERNALS from './../../internals';
import { isIOS } from './../../utils';
import PACKAGE from './../../../package.json';
import INTERNALS from '../../utils/internals';
import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
import type App from '../core/firebase-app';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class RNFirebaseUtils {
static _NAMESPACE = 'utils';
static _NATIVE_DISABLED = true;
static _NATIVE_MODULE = 'RNFirebaseUtils';
type GoogleApiAvailabilityType = {
status: number,
isAvailable: boolean,
isUserResolvableError?: boolean,
hasResolution?: boolean,
error?: string
}
export const MODULE_NAME = 'RNFirebaseUtils';
export const NAMESPACE = 'utils';
export default class RNFirebaseUtils extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
/**
*
*/
checkPlayServicesAvailability() {
if (isIOS) return null;
if (isIOS) return;
const code = this.playServicesAvailability.code;
const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
this.promptForPlayServices();
} else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(code);
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
if (code === 2) console.warn(error); // only warn if it exists but may need an update
if (status === 2) console.warn(error); // only warn if it exists but may need an update
else throw new Error(error);
} else {
console.warn(error);
}
}
}
return null;
}
promptForPlayServices() {
@ -54,41 +65,13 @@ export default class RNFirebaseUtils {
return FirebaseCoreModule.makePlayServicesAvailable();
}
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
/**
* Set the global logging level for all logs.
*
* @param booleanOrDebugString
* @param logLevel
*/
set logLevel(booleanOrDebugString) {
INTERNALS.OPTIONS.logLevel = booleanOrDebugString;
}
/**
* Returns an array of all current database registrations id strings
*/
get databaseRegistrations(): Array<string> {
return Object.keys(INTERNALS.SyncTree._reverseLookup);
}
/**
* Call with a registration id string to get the details off this reg
*/
get getDatabaseRegistrationDetails(): Function {
return INTERNALS.SyncTree.getRegistration.bind(INTERNALS.SyncTree);
}
/**
* Accepts an array or a single string of registration ids.
* This will remove the refs on both the js and native sides and their listeners.
* @return {function(this:T)}
*/
get removeDatabaseRegistration(): Function {
return INTERNALS.SyncTree.removeListenersForRegistrations.bind(INTERNALS.SyncTree);
set logLevel(logLevel: string) {
INTERNALS.OPTIONS.logLevel = logLevel;
}
/**
@ -105,7 +88,7 @@ export default class RNFirebaseUtils {
* @android
* @param bool
*/
set errorOnMissingPlayServices(bool: Boolean) {
set errorOnMissingPlayServices(bool: boolean) {
INTERNALS.OPTIONS.errorOnMissingPlayServices = bool;
}
@ -114,18 +97,10 @@ export default class RNFirebaseUtils {
* @android
* @param bool
*/
set promptOnMissingPlayServices(bool: Boolean) {
set promptOnMissingPlayServices(bool: boolean) {
INTERNALS.OPTIONS.promptOnMissingPlayServices = bool;
}
}
export const statics = {
DEFAULT_APP_NAME: INTERNALS.STRINGS.DEFAULT_APP_NAME,
// VERSIONS: {
// react: ReactVersion,
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),
// 'react-native-firebase': PACKAGE.version,
// },
};
export const statics = {};

226
lib/types/index.js Normal file
View File

@ -0,0 +1,226 @@
/* @flow */
import type AdMob from '../modules/admob';
import { typeof statics as AdMobStatics } from '../modules/admob';
import type Analytics from '../modules/analytics';
import { typeof statics as AnalyticsStatics } from '../modules/analytics';
import type Auth from '../modules/auth';
import { typeof statics as AuthStatics } from '../modules/auth';
import type Config from '../modules/config';
import { typeof statics as ConfigStatics } from '../modules/config';
import type Crash from '../modules/crash';
import { typeof statics as CrashStatics } from '../modules/crash';
import type Crashlytics from '../modules/fabric/crashlytics';
import { typeof statics as CrashlyticsStatics } from '../modules/fabric/crashlytics';
import type Database from '../modules/database';
import { typeof statics as DatabaseStatics } from '../modules/database';
import type Firestore from '../modules/firestore';
import { typeof statics as FirestoreStatics } from '../modules/firestore';
import type Links from '../modules/links';
import { typeof statics as LinksStatics } from '../modules/links';
import type Messaging from '../modules/messaging';
import { typeof statics as MessagingStatics } from '../modules/messaging';
import type ModuleBase from '../utils/ModuleBase';
import type Performance from '../modules/perf';
import { typeof statics as PerformanceStatics } from '../modules/perf';
import type Storage from '../modules/storage';
import { typeof statics as StorageStatics } from '../modules/storage';
import type Utils from '../modules/utils';
import { typeof statics as UtilsStatics } from '../modules/utils';
/* Core types */
export type FirebaseError = {
message: string,
name: string,
code: string,
stack: string,
path: string,
details: string,
modifiers: string
}
export type FirebaseModule = $Subtype<ModuleBase>;
export type FirebaseModuleConfig = {
events?: string[],
moduleName: FirebaseModuleName,
multiApp: boolean,
namespace: FirebaseNamespace,
}
export type FirebaseModuleName = 'RNFirebaseAdmob' | 'RNFirebaseAnalytics' | 'RNFirebaseAuth'
| 'RNFirebaseRemoteConfig' | 'RNFirebaseCrash' | 'RNFirebaseCrashlytics' | 'RNFirebaseDatabase'
| 'RNFirebaseFirestore' | 'RNFirebaseLinks' | 'RNFirebaseMessaging' | 'RNFirebasePerformance'
| 'RNFirebaseStorage' | 'RNFirebaseUtils';
export type FirebaseNamespace = 'admob' | 'analytics' | 'auth' | 'config' | 'crash'
| 'crashlytics' | 'database' | 'firestore' | 'links' | 'messaging' | 'perf' | 'storage'
| 'utils';
export type FirebaseOptions = {
apiKey: string,
appId: string,
databaseURL: string,
messagingSenderId: string,
projectId: string,
storageBucket: string,
}
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {
(): M,
nativeModuleExists: boolean,
} & S;
export type FirebaseStatics = $Subtype<Object>;
/* Admob types */
export type AdMobModule = {
(): AdMob,
nativeModuleExists: boolean,
} & AdMobStatics;
/* Analytics types */
export type AnalyticsModule = {
(): Analytics,
nativeModuleExists: boolean,
} & AnalyticsStatics;
/* Remote Config types */
export type ConfigModule = {
(): Config,
nativeModuleExists: boolean,
} & ConfigStatics;
export type AuthCredential = {
providerId: string,
token: string,
secret: string
}
/* Auth types */
export type AuthModule = {
(): Auth,
nativeModuleExists: boolean,
} & AuthStatics;
export type ActionCodeSettings = {
android: {
installApp?: boolean,
minimumVersion?: string,
packageName: string,
},
handleCodeInApp?: boolean,
iOS: {
bundleId?: string,
},
url: string,
}
/* Crash types */
export type CrashModule = {
(): Crash,
nativeModuleExists: boolean,
} & CrashStatics;
/* Database types */
export type DatabaseModule = {
(): Database,
nativeModuleExists: boolean,
} & DatabaseStatics;
export type DatabaseModifier = {
id: string;
type: 'orderBy' | 'limit' | 'filter';
name?: string;
key?: string;
limit?: number;
value?: any;
valueType?: string;
}
/* Fabric types */
export type CrashlyticsModule = {
(): Crashlytics,
nativeModuleExists: boolean,
} & CrashlyticsStatics;
export type FabricModule = {
crashlytics: CrashlyticsModule,
}
/* Firestore types */
export type FirestoreModule = {
(): Firestore,
nativeModuleExists: boolean,
} & FirestoreStatics;
export type FirestoreNativeDocumentChange = {
document: FirestoreNativeDocumentSnapshot,
newIndex: number,
oldIndex: number,
type: string,
}
export type FirestoreNativeDocumentSnapshot = {
data: { [string]: FirestoreTypeMap },
metadata: FirestoreSnapshotMetadata,
path: string,
}
export type FirestoreSnapshotMetadata = {
fromCache: boolean,
hasPendingWrites: boolean,
}
export type FirestoreQueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
export type FirestoreQueryOperator = '<' | '<=' | '=' | '==' | '>' | '>=';
export type FirestoreTypeMap = {
type: 'array' | 'boolean' | 'date' | 'fieldvalue' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
value: any,
}
export type FirestoreWriteOptions = {
merge?: boolean,
}
/* Links types */
export type LinksModule = {
(): Links,
nativeModuleExists: boolean,
} & LinksStatics;
/* Messaging types */
export type MessagingModule = {
(): Messaging,
nativeModuleExists: boolean,
} & MessagingStatics;
/* Performance types */
export type PerformanceModule = {
(): Performance,
nativeModuleExists: boolean,
} & PerformanceStatics;
/* Storage types */
export type StorageModule = {
(): Storage,
nativeModuleExists: boolean,
} & StorageStatics;
/* Utils types */
export type UtilsModule = {
(): Utils,
nativeModuleExists: boolean,
} & UtilsStatics;

View File

@ -1,179 +1,42 @@
/**
* @flow
*/
import { NativeModules } from 'react-native';
import { initialiseLogger } from './log';
import { initialiseNativeModule } from './native';
import Log from '../utils/log';
import INTERNALS from './../internals';
import FirebaseCore from './../firebase';
import FirebaseApp from '../firebase-app';
import { nativeWithApp } from './../utils';
const logs = {};
// Firebase Native SDKs that support multiple app instances
const MULTI_APP_MODULES = [
'auth',
'database',
'firestore',
'storage',
];
const NATIVE_MODULE_EVENTS = {
Storage: [
'storage_event',
'storage_error',
],
Auth: [
'auth_state_changed',
'phone_auth_state_changed',
],
Database: [
'database_transaction_event',
// 'database_server_offset', // TODO
],
Firestore: [
'firestore_collection_sync_event',
'firestore_document_sync_event',
],
};
const DEFAULTS = {
Database: {
persistence: false,
},
};
import type App from '../modules/core/firebase-app';
import type { FirebaseModuleConfig, FirebaseNamespace } from '../types';
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;
_app: App;
namespace: FirebaseNamespace;
/**
*
* @param firebaseApp
* @param options
* @param withEventEmitter
* @param app
* @param config
*/
constructor(firebaseApp: Object, options: Object, withEventEmitter: boolean = false) {
this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', '');
this._firebaseApp = firebaseApp;
this._appName = firebaseApp._name;
this._namespace = `${this._appName}:${this._module}`;
this._options = Object.assign({}, DEFAULTS[this._module] || {}, options);
constructor(app: App, config: FirebaseModuleConfig) {
if (!config.moduleName) {
throw new Error('Missing module name');
}
if (!config.namespace) {
throw new Error('Missing namespace');
}
const { moduleName } = config;
this._app = app;
this.namespace = config.namespace;
// check if native module exists as all native
// modules are now optionally part of build
const nativeModule = NativeModules[this.constructor._NATIVE_MODULE];
if (!nativeModule) {
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_MODULE(
this.constructor._NAMESPACE,
this.constructor._NATIVE_MODULE,
),
);
}
// used by the modules that extend ModuleBase
// to access their native module counterpart
if (!MULTI_APP_MODULES.includes(this._module.toLowerCase())) {
this._native = nativeModule;
} else {
this._native = nativeWithApp(this._appName, nativeModule);
}
if (withEventEmitter) {
this._setupEventEmitter(nativeModule, this._module);
}
initialiseNativeModule(this, config);
initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
}
/**
*
* @param nativeModule
* @param moduleName
* @private
*/
_setupEventEmitter(nativeModule, moduleName) {
this._eventEmitter = FirebaseCore._getOrSetNativeEmitter(`${this._appName}-${this._module}`, nativeModule);
const events = NATIVE_MODULE_EVENTS[moduleName];
if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) {
FirebaseCore._subscribeForDistribution(events[i], this._eventEmitter);
}
}
}
/**
*
* @param eventName
* @return {string}
* @private
*/
_getAppEventName(eventName) {
return `${this._appName}-${eventName}`;
}
/**
* Returns the FirebaseApp instance for current module
* Returns the App instance for current module
* @return {*}
*/
get app(): FirebaseApp {
return this._firebaseApp;
}
get log(): Log {
if (logs[this._namespace]) return logs[this._namespace];
return logs[this._namespace] = Log.createLogger(
`🔥 ${this._namespace.toUpperCase()}`,
);
}
/*
* Proxy functions to shared event emitter instance
* https://github.com/facebook/react-native/blob/master/Libraries/EventEmitter/EventEmitter.js
*/
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
get addListener(): Function {
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
}
get once(): Function {
return INTERNALS.SharedEventEmitter.once.bind(INTERNALS.SharedEventEmitter);
}
get on(): Function {
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
}
get emit(): Function {
return INTERNALS.SharedEventEmitter.emit.bind(INTERNALS.SharedEventEmitter);
}
get listeners(): Function {
return INTERNALS.SharedEventEmitter.listeners.bind(INTERNALS.SharedEventEmitter);
}
hasListeners(eventType: string): Boolean {
const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(eventType);
return subscriptions && subscriptions.length;
}
get removeListener(): Function {
return INTERNALS.SharedEventEmitter.removeListener.bind(INTERNALS.SharedEventEmitter);
}
get removeAllListeners(): Function {
return INTERNALS.SharedEventEmitter.removeAllListeners.bind(INTERNALS.SharedEventEmitter);
get app(): App {
return this._app;
}
}

View File

@ -2,8 +2,9 @@
* @flow
*/
export default class ReferenceBase {
constructor(path: string, module) {
this._module = module;
path: string;
constructor(path: string) {
this.path = path || '/';
}
@ -16,8 +17,4 @@ export default class ReferenceBase {
get key(): string | null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
}
get log() {
return this._module.log;
}
}

View File

@ -1,18 +1,23 @@
import { NativeEventEmitter } from 'react-native';
/**
* @flow
*/
import { NativeEventEmitter, NativeModules } from 'react-native';
import INTERNALS from './../internals';
import DatabaseSnapshot from './../modules/database/snapshot';
import DatabaseReference from './../modules/database/reference';
import { isString, nativeToJSError } from './../utils';
import { SharedEventEmitter } from './events';
import DatabaseSnapshot from '../modules/database/snapshot';
import DatabaseReference from '../modules/database/reference';
import { isString, nativeToJSError } from '../utils';
type Listener = (DatabaseSnapshot) => any;
type Registration = {
key: String,
path: String,
once?: Boolean,
appName: String,
eventType: String,
listener: Function,
eventRegistrationKey: String,
key: string,
path: string,
once?: boolean,
appName: string,
eventType: string,
listener: Listener,
eventRegistrationKey: string,
ref: DatabaseReference,
}
@ -20,16 +25,21 @@ type Registration = {
* Internally used to manage firebase database realtime event
* subscriptions and keep the listeners in sync in js vs native.
*/
export default class SyncTree {
constructor(databaseNative) {
class SyncTree {
_nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: { [string]: Listener }}};
constructor() {
this._tree = {};
this._reverseLookup = {};
this._databaseNative = databaseNative;
this._nativeEmitter = new NativeEventEmitter(databaseNative);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
);
if (NativeModules.RNFirebaseDatabase) {
this._nativeEmitter = new NativeEventEmitter(NativeModules.RNFirebaseDatabase);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
);
}
}
/**
@ -63,13 +73,13 @@ export default class SyncTree {
// notify native that the registration
// no longer exists so it can remove
// the native listeners
return this._databaseNative.off(key, eventRegistrationKey);
return NativeModules.RNFirebaseDatabase.off(key, eventRegistrationKey);
}
const { snapshot, previousChildName } = event.data;
// forward on to users .on(successCallback <-- listener
return INTERNALS.SharedEventEmitter.emit(
return SharedEventEmitter.emit(
eventRegistrationKey,
new DatabaseSnapshot(registration.ref, snapshot),
previousChildName,
@ -96,7 +106,7 @@ export default class SyncTree {
const error = nativeToJSError(code, message, { ref: registration.ref });
// forward on to users .on(successCallback, cancellationCallback <-- listener
INTERNALS.SharedEventEmitter.emit(registrationCancellationKey, error);
SharedEventEmitter.emit(registrationCancellationKey, error);
// remove the paired event registration - if we received a cancellation
// event then it's guaranteed that they'll be no further value events
@ -110,7 +120,7 @@ export default class SyncTree {
* @param registration
* @return {null}
*/
getRegistration(registration): Registration | null {
getRegistration(registration: string): Registration | null {
return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null;
}
@ -120,17 +130,17 @@ export default class SyncTree {
* @param registrations
* @return {number}
*/
removeListenersForRegistrations(registrations) {
removeListenersForRegistrations(registrations: string | string[]): number {
if (isString(registrations)) {
this.removeRegistration(registrations);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations);
SharedEventEmitter.removeAllListeners(registrations);
return 1;
}
if (!Array.isArray(registrations)) return 0;
for (let i = 0, len = registrations.length; i < len; i++) {
this.removeRegistration(registrations[i]);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations[i]);
SharedEventEmitter.removeAllListeners(registrations[i]);
}
return registrations.length;
@ -143,13 +153,13 @@ export default class SyncTree {
* @param registrations
* @return {Array} array of registrations removed
*/
removeListenerRegistrations(listener, registrations) {
removeListenerRegistrations(listener: () => any, registrations: string[]) {
if (!Array.isArray(registrations)) return [];
const removed = [];
for (let i = 0, len = registrations.length; i < len; i++) {
const registration = registrations[i];
const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
if (subscriptions) {
for (let j = 0, l = subscriptions.length; j < l; j++) {
const subscription = subscriptions[j];
@ -173,7 +183,7 @@ export default class SyncTree {
* @param path
* @return {Array}
*/
getRegistrationsByPath(path): Array {
getRegistrationsByPath(path: string): string[] {
const out = [];
const eventKeys = Object.keys(this._tree[path] || {});
@ -191,7 +201,7 @@ export default class SyncTree {
* @param eventType
* @return {Array}
*/
getRegistrationsByPathEvent(path, eventType): Array {
getRegistrationsByPathEvent(path: string, eventType: string): string[] {
if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return [];
@ -206,9 +216,9 @@ export default class SyncTree {
* @param listener
* @return {Array}
*/
getOneByPathEventListener(path: string, eventType: string, listener: Function): Array {
if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return [];
getOneByPathEventListener(path: string, eventType: string, listener: Function): ?string {
if (!this._tree[path]) return null;
if (!this._tree[path][eventType]) return null;
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
@ -228,22 +238,28 @@ export default class SyncTree {
* @param listener
* @return {String}
*/
addRegistration(parameters: Registration, listener): String {
const { path, eventType, eventRegistrationKey, once } = parameters;
addRegistration(registration: Registration): string {
const {
eventRegistrationKey,
eventType,
listener,
once,
path,
} = registration;
if (!this._tree[path]) this._tree[path] = {};
if (!this._tree[path][eventType]) this._tree[path][eventType] = {};
this._tree[path][eventType][eventRegistrationKey] = listener;
this._reverseLookup[eventRegistrationKey] = Object.assign({ listener }, parameters);
this._reverseLookup[eventRegistrationKey] = registration;
if (once) {
INTERNALS.SharedEventEmitter.once(
SharedEventEmitter.once(
eventRegistrationKey,
this._onOnceRemoveRegistration(eventRegistrationKey, listener),
);
} else {
INTERNALS.SharedEventEmitter.addListener(eventRegistrationKey, listener);
SharedEventEmitter.addListener(eventRegistrationKey, listener);
}
return eventRegistrationKey;
@ -256,7 +272,7 @@ export default class SyncTree {
* @param registration
* @return {boolean}
*/
removeRegistration(registration: String): Boolean {
removeRegistration(registration: string): boolean {
if (!this._reverseLookup[registration]) return false;
const { path, eventType, once } = this._reverseLookup[registration];
@ -274,7 +290,7 @@ export default class SyncTree {
// automatically unsubscribed on native when the first event is sent
const registrationObj = this._reverseLookup[registration];
if (registrationObj && !once) {
this._databaseNative.off(registrationObj.key, registration);
NativeModules.RNFirebaseDatabase.off(registrationObj.key, registration);
}
delete this._tree[path][eventType][registration];
@ -292,10 +308,11 @@ export default class SyncTree {
* @private
*/
_onOnceRemoveRegistration(registration, listener) {
return (...args) => {
return (...args: any[]) => {
this.removeRegistration(registration);
listener(...args);
};
}
}
export default new SyncTree();

173
lib/utils/apps.js Normal file
View File

@ -0,0 +1,173 @@
/**
* @flow
*/
import { NativeModules } from 'react-native';
import App from '../modules/core/firebase-app';
import INTERNALS from './internals';
import { isAndroid, isObject, isString } from './';
import type {
FirebaseModule,
FirebaseModuleAndStatics,
FirebaseModuleName,
FirebaseNamespace,
FirebaseOptions,
FirebaseStatics,
} from '../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule }} = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
export default {
DEFAULT_APP_NAME,
app(name?: string): App {
const _name = name ? name.toUpperCase() : DEFAULT_APP_NAME;
const app = APPS[_name];
if (!app) throw new Error(INTERNALS.STRINGS.ERROR_APP_NOT_INIT(_name));
return app;
},
apps(): Array<App> {
// $FlowBug: Object.values always returns mixed type: https://github.com/facebook/flow/issues/2221
return Object.values(APPS);
},
/**
*
* @param statics
* @param InstanceClass
* @return {function()}
* @private
*/
appModule<M: FirebaseModule>(app: App, namespace: FirebaseNamespace, InstanceClass: Class<M>): () => FirebaseModule {
return (): M => {
if (!APP_MODULES[app]) {
APP_MODULES[app] = {};
}
if (isAndroid && namespace !== 'utils' && !INTERNALS.FLAGS.checkedPlayServices) {
INTERNALS.FLAGS.checkedPlayServices = true;
app.utils().checkPlayServicesAvailability();
}
if (!APP_MODULES[app][namespace]) {
APP_MODULES[app][namespace] = new InstanceClass(app, app.options);
}
return APP_MODULES[app][namespace];
};
},
deleteApp(name: string): Promise<boolean> {
const app = APPS[name];
if (!app) return Promise.resolve(true);
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete
return app.delete().then(() => {
delete APPS[name];
return true;
});
},
/**
* Web SDK initializeApp
*
* @param options
* @param name
* @return {*}
*/
initializeApp(options: FirebaseOptions, name: string): App {
if (name && !isString(name)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
}
const _name = (name || DEFAULT_APP_NAME).toUpperCase();
// return an existing app if found
// todo in v4 remove deprecation and throw an error
if (APPS[_name]) {
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
return APPS[_name];
}
// only validate if app doesn't already exist
// to allow apps already initialized natively
// to still go through init without erroring (backwards compatibility)
if (!isObject(options)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_OBJECT);
}
if (!options.apiKey) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('apiKey'));
}
if (!options.appId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('appId'));
}
if (!options.databaseURL) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('databaseURL'));
}
if (!options.messagingSenderId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('messagingSenderId'));
}
if (!options.projectId) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('projectId'));
}
if (!options.storageBucket) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('storageBucket'));
}
APPS[_name] = new App(_name, options);
return APPS[_name];
},
/**
* Bootstraps all native app instances that were discovered on boot
*/
initializeNativeApps() {
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
const app = FirebaseCoreModule.apps[i];
const options = Object.assign({}, app);
delete options.name;
APPS[app.name] = new App(app.name, options, true);
}
},
/**
*
* @param statics
* @param InstanceClass
* @return {function(App=)}
*/
moduleAndStatics<M: FirebaseModule, S: FirebaseStatics>(namespace: FirebaseNamespace, statics: S, moduleName: FirebaseModuleName): FirebaseModuleAndStatics<M, S> {
const getModule = (app?: App): FirebaseModule => {
let _app = app;
// throw an error if it's not a valid app instance
if (_app && !(_app instanceof App)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
// default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
else if (!_app) _app = this.app(DEFAULT_APP_NAME);
if (namespace === 'crashlytics') {
return _app.fabric[namespace]();
}
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
const module = _app[namespace];
return module();
};
return Object.assign(getModule, statics, {
nativeModuleExists: !!NativeModules[moduleName],
});
},
};

62
lib/utils/events.js Normal file
View File

@ -0,0 +1,62 @@
/**
* @flow
*/
import { NativeEventEmitter, NativeModules } from 'react-native';
import EventEmitter from './emitter/EventEmitter';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig, FirebaseModuleName } from '../types';
const NATIVE_EMITTERS: { [string]: NativeEventEmitter } = {};
const NATIVE_SUBSCRIPTIONS: { [string]: boolean } = {};
export const SharedEventEmitter = new EventEmitter();
export const getAppEventName = (module: ModuleBase, eventName: string): string => {
return `${module.app.name}-${eventName}`;
};
const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): NativeEventEmitter => {
const name = `${module.app.name}-${moduleName}`;
const nativeModule = NativeModules[moduleName];
if (!NATIVE_EMITTERS[name]) {
NATIVE_EMITTERS[name] = new NativeEventEmitter(nativeModule);
}
return NATIVE_EMITTERS[name];
};
/**
* Subscribe to a native event for js side distribution by appName
* React Native events are hard set at compile - cant do dynamic event names
* so we use a single event send it to js and js then internally can prefix it
* and distribute dynamically.
*
* @param module
* @param eventName
* @private
*/
const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: ModuleBase, eventName: string): void => {
if (!NATIVE_SUBSCRIPTIONS[eventName]) {
const nativeEmitter = getNativeEmitter(moduleName, module);
nativeEmitter.addListener(eventName, (event) => {
if (event.appName) {
// native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
} else {
// standard event - no need to prefix
SharedEventEmitter.emit(eventName, event);
}
});
NATIVE_SUBSCRIPTIONS[eventName] = true;
}
};
export const initialiseNativeModuleEventEmitter = (module: ModuleBase, config: FirebaseModuleConfig): void => {
const { events, moduleName } = config;
if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) {
subscribeToNativeModuleEvents(moduleName, module, events[i]);
}
}
};

View File

@ -6,18 +6,10 @@ import { Platform } from 'react-native';
// modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const hasOwnProperty = Object.hasOwnProperty;
const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50;
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
const REGEXP_FIELD_NAME = new RegExp(
`^(?:\\.?((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))$`
);
const REGEXP_FIELD_PATH = new RegExp(
`^((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+)(?:\\.((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))*$`
);
/**
* Deep get a value from an object.
* @website https://github.com/Salakar/deeps
@ -26,9 +18,7 @@ const REGEXP_FIELD_PATH = new RegExp(
* @param joiner
* @returns {*}
*/
export function deepGet(object: Object,
path: string,
joiner?: string = '/'): any {
export function deepGet(object: Object, path: string, joiner?: string = '/'): any {
const keys = path.split(joiner);
let i = 0;
@ -52,9 +42,7 @@ export function deepGet(object: Object,
* @param joiner
* @returns {*}
*/
export function deepExists(object: Object,
path: string,
joiner?: string = '/'): boolean {
export function deepExists(object: Object, path: string, joiner?: string = '/'): boolean {
const keys = path.split(joiner);
let i = 0;
@ -98,7 +86,7 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2
* @returns {boolean}
*/
export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean {
export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false;
}
@ -112,8 +100,8 @@ export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean {
* @param item
* @returns {boolean}
*/
export function isObject(item: any): boolean {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
export function isObject(item: mixed): boolean %checks {
return item ? (typeof item === 'object' && !Array.isArray(item) && item !== null) : false;
}
/**
@ -121,8 +109,8 @@ export function isObject(item: any): boolean {
* @param item
* @returns {*|boolean}
*/
export function isFunction(item?: any): boolean {
return Boolean(item && typeof item === 'function');
export function isFunction(item?: mixed): boolean %checks {
return item ? typeof item === 'function' : false;
}
/**
@ -130,20 +118,10 @@ export function isFunction(item?: any): boolean {
* @param value
* @return {boolean}
*/
export function isString(value: any): boolean {
export function isString(value: mixed): boolean %checks {
return typeof value === 'string';
}
/**
* Firestore field name/path validator.
* @param field
* @param paths
* @return {boolean}
*/
export function isValidFirestoreField(field, paths) {
return (paths ? REGEXP_FIELD_PATH : REGEXP_FIELD_NAME).test(field);
}
// platform checks
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
@ -167,7 +145,7 @@ export function tryJSONParse(string: string | null): any {
* @param data
* @returns {*}
*/
export function tryJSONStringify(data: any): string | null {
export function tryJSONStringify(data: mixed): string | null {
try {
return JSON.stringify(data);
} catch (jsonError) {
@ -362,25 +340,6 @@ export function nativeToJSError(code: string, message: string, additionalProps?:
return error;
}
/**
* Prepends appName arg to all native method calls
* @param appName
* @param NativeModule
*/
export function nativeWithApp(appName: string, NativeModule: Object) {
const native = {};
const methods = Object.keys(NativeModule);
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
}
return native;
}
/**
*
* @param object
@ -412,21 +371,21 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback
* @return {Promise}
*/
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function) {
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> {
if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback.length === 1) {
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else {
} else if (optionalCallback) {
optionalCallback(null, result);
}
return Promise.resolve(result);
}).catch((error) => {
optionalCallback(error);
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
}

View File

@ -1,9 +1,7 @@
import { Platform, NativeModules } from 'react-native';
import EventEmitter from './utils/emitter/EventEmitter';
import SyncTree from './utils/SyncTree';
const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
/**
* @flow
*/
import { Platform } from 'react-native';
const NAMESPACE_PODS = {
admob: 'Firebase/AdMob',
@ -23,26 +21,32 @@ const GRADLE_DEPS = {
};
const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
1: {
code: 'SERVICE_MISSING',
message: 'Google Play services is missing on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
3: {
code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
9: {
code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
18: {
code: 'SERVICE_UPDATING',
message: 'Google Play services is currently being updated on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
19: {
code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.',
@ -61,11 +65,6 @@ export default {
checkedPlayServices: false,
},
// track all initialized firebase apps
APPS: {
[DEFAULT_APP_NAME]: null,
},
STRINGS: {
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' +
'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.',
@ -92,35 +91,35 @@ export default {
/**
* @return {string}
*/
ERROR_MISSING_CB(method) {
ERROR_MISSING_CB(method: string) {
return `Missing required callback for method ${method}().`;
},
/**
* @return {string}
*/
ERROR_MISSING_ARG(type, method) {
ERROR_MISSING_ARG(type: string, method: string) {
return `Missing required argument of type '${type}' for method '${method}()'.`;
},
/**
* @return {string}
*/
ERROR_MISSING_ARG_NAMED(name, type, method) {
ERROR_MISSING_ARG_NAMED(name: string, type: string, method: string) {
return `Missing required argument '${name}' of type '${type}' for method '${method}()'.`;
},
/**
* @return {string}
*/
ERROR_ARG_INVALID_VALUE(name, expected, got) {
ERROR_ARG_INVALID_VALUE(name: string, expected: string, got: string) {
return `Invalid value for argument '${name}' expected value '${expected}' but got '${got}'.`;
},
/**
* @return {string}
*/
ERROR_PROTECTED_PROP(name) {
ERROR_PROTECTED_PROP(name: string) {
return `Property '${name}' is protected and can not be overridden by extendApp.`;
},
@ -129,7 +128,7 @@ export default {
* @param namespace
* @param nativeModule
*/
ERROR_MISSING_MODULE(namespace, nativeModule) {
ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
const snippet = `firebase.${namespace}()`;
if (Platform.OS === 'ios') {
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
@ -151,7 +150,7 @@ export default {
/**
* @return {string}
*/
ERROR_APP_NOT_INIT(appName) {
ERROR_APP_NOT_INIT(appName: string) {
return `The [${appName}] firebase app has not been initialized!`;
},
@ -160,43 +159,43 @@ export default {
* @return {string}
* @constructor
*/
ERROR_MISSING_OPT(optName) {
ERROR_MISSING_OPT(optName: string) {
return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`;
},
/**
* @return {string}
*/
ERROR_NOT_APP(namespace) {
return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`;
ERROR_NOT_APP(namespace: string) {
return `Invalid App instance passed to firebase.${namespace}(app <--).`;
},
/**
* @return {string}
*/
ERROR_UNSUPPORTED_CLASS_METHOD(className, method) {
ERROR_UNSUPPORTED_CLASS_METHOD(className: string, method: string) {
return `${className}.${method}() is unsupported by the native Firebase SDKs.`;
},
/**
* @return {string}
*/
ERROR_UNSUPPORTED_CLASS_PROPERTY(className, property) {
ERROR_UNSUPPORTED_CLASS_PROPERTY(className: string, property: string) {
return `${className}.${property} is unsupported by the native Firebase SDKs.`;
},
/**
* @return {string}
*/
ERROR_UNSUPPORTED_MODULE_METHOD(module, method) {
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`;
ERROR_UNSUPPORTED_MODULE_METHOD(namespace: string, method: string) {
return `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
},
/**
* @return {string}
*/
ERROR_PLAY_SERVICES(statusCode) {
ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode];
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
@ -208,32 +207,13 @@ export default {
return `${start}\r\n\r\n` +
'-------------------------\r\n' +
(knownError ?
`${knownError.code}: ${knownError.message} (code ${statusCode})` :
`A specific play store availability reason reason was not available (unknown code: ${statusCode || null})`
`${knownError.code}: ${knownError.message} (code ${statusCode})` :
`A specific play store availability reason reason was not available (unknown code: ${statusCode})`
) +
'\r\n-------------------------' +
'\r\n\r\n' +
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' +
'\r\n\r\nhttp://invertase.link/play-services';
},
DEFAULT_APP_NAME,
},
SharedEventEmitter: new EventEmitter(),
SyncTree: NativeModules.RNFirebaseDatabase ? new SyncTree(NativeModules.RNFirebaseDatabase) : null,
// internal utils
deleteApp(name: String) {
const app = this.APPS[name];
if (!app) return Promise.resolve();
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete
return app.delete().then(() => {
delete this.APPS[name];
return true;
});
},
};

View File

@ -1,12 +1,34 @@
/*
* @flow
*/
import { windowOrGlobal } from './';
import type ModuleBase from './ModuleBase';
((base) => {
window = base || window;
// $FlowFixMe: Why are we using localStorage at all?
if (!window.localStorage) window.localStorage = {};
})(windowOrGlobal);
// clean up time
const NATIVE_LOGGERS: { [string]: Object } = {};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
export const getLogger = (module: ModuleBase) => {
const key = getModuleKey(module);
return NATIVE_LOGGERS[key];
};
export const initialiseLogger = (module: ModuleBase, logNamespace: string) => {
const key = getModuleKey(module);
if (!NATIVE_LOGGERS[key]) {
NATIVE_LOGGERS[key] = require('bows')(`🔥 ${logNamespace.toUpperCase()}`);
}
};
export default class Log {
static createLogger(namespace) {
return require('bows')(namespace);

59
lib/utils/native.js Normal file
View File

@ -0,0 +1,59 @@
/*
* @flow
*/
import { NativeModules } from 'react-native';
import { initialiseNativeModuleEventEmitter } from './events';
import INTERNALS from './internals';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig } from '../types';
const NATIVE_MODULES: { [string]: Object } = {};
/**
* Prepends appName arg to all native method calls
* @param appName
* @param NativeModule
*/
const nativeWithApp = (appName: string, NativeModule: Object): Object => {
const native = {};
const methods = Object.keys(NativeModule);
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
}
return native;
};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
export const getNativeModule = (module: ModuleBase): Object => {
const key = getModuleKey(module);
return NATIVE_MODULES[key];
};
export const initialiseNativeModule = (module: ModuleBase, config: FirebaseModuleConfig): Object => {
const { moduleName, multiApp, namespace } = config;
const nativeModule = NativeModules[moduleName];
const key = getModuleKey(module);
if (!nativeModule && namespace !== 'utils') {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(namespace, moduleName));
}
// used by the modules that extend ModuleBase
// to access their native module counterpart
if (multiApp) {
NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule);
} else {
NATIVE_MODULES[key] = nativeModule;
}
initialiseNativeModuleEventEmitter(module, config);
return NATIVE_MODULES[key];
};

3576
package-lock.json generated

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More