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/extensions": 0,
"import/no-unresolved": 0, "import/no-unresolved": 0,
"import/no-extraneous-dependencies": 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": { "globals": {
"__DEV__": true, "__DEV__": true,

View File

@ -1,108 +1,77 @@
[ignore] [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 ; Ignore unexpected extra "@providesModule"
.*/node_modules/node-haste/.* .*/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 # React Native problems
.*/node_modules/react-native/Libraries/Animated/src/AnimatedInterpolation.js .*/node_modules/metro-bundler/src/DeltaBundler/DeltaCalculator.js.flow
.*/node_modules/react-native/Libraries/Animated/src/Interpolation.js .*/node_modules/metro-bundler/src/DeltaBundler/DeltaPatcher.js.flow
.*/node_modules/react-native/Libraries/BugReporting/dumpReactTree.js .*/node_modules/metro-bundler/src/node-haste/AssetResolutionCache.js.flow
.*/node_modules/react-native/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js .*/node_modules/metro-bundler/src/node-haste/DependencyGraph.js.flow
.*/node_modules/react-native/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolater.js #.*/node_modules/react-native/Libraries/Animated/src/nodes/AnimatedStyle.js
.*/node_modules/react-native/Libraries/Experimental/WindowedListView.js #.*/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
.*/node_modules/react-native/Libraries/Image/Image.io.js #.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableFlatList.js
.*/node_modules/react-native/Libraries/NavigationExperimental/NavigationExperimental.js #.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableListView.js
.*/node_modules/react-native/Libraries/NavigationExperimental/NavigationHeaderStyleInterpolator.js #.*/node_modules/react-native/Libraries/Image/ImageBackground.js
.*/node_modules/react-native/Libraries/Network/FormData.js #.*/node_modules/react-native/Libraries/Lists/FlatList.js
.*/node_modules/react-native/Libraries/ReactIOS/YellowBox.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 tests project
# Ignore react and fbjs where there are overlaps, but don't ignore .*/tests/.*
# 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
[include] [include]
node_modules/fbjs/lib
[libs] [libs]
lib/flow.js
node_modules/react-native/Libraries/react-native/react-native-interface.js node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow node_modules/react-native/flow/
node_modules/fbjs/flow/lib
[options] [options]
module.system=haste module.system=haste
experimental.strict_type_args=true emoji=true
unsafe.enable_getters_and_setters=true
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
munge_underscores=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.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=$FlowIssue
suppress_type=$FlowFixMe suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe 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\\)*\\$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\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 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\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
unsafe.enable_getters_and_setters=true
[version] [version]
^0.46.0 ^0.56.0

View File

@ -1,14 +1,13 @@
<!--- <!---
- BEFORE YOU MAKE AN ISSUE - BEFORE YOU MAKE AN ISSUE
-->
<!--- The issue list of this repo is exclusively for bug reports.
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
--->
<!--- 1) For feature requests, please use our Canny board: https://react-native-firebase.canny.io/feature-requests
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.
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 ### Issue
@ -38,4 +37,4 @@
6. Firebase Module: 6. Firebase Module:
<!-- Love react-native-firebase? Please consider supporting our collective: <!-- Love react-native-firebase? Please consider supporting our collective:
👉 https://opencollective.com/react-native-firebase/donate --> 👉 https://opencollective.com/react-native-firebase/donate -->

3
.gitignore vendored
View File

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

View File

@ -11,6 +11,15 @@ npm-debug.log
project.xcworkspace/ project.xcworkspace/
xcuserdata/ xcuserdata/
# Config files
.babelrc
.editorconfig
.eslintrc
.flowconfig
.watchmanconfig
buddybuild_postclone.sh
jsconfig.json
# Example # Example
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 > '**?**' indicates partial support
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | Web SDK | | Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK |
| ---------------------- | :---: | :---: | :---: | :---: | :---: | | ---------------------- | :---: | :---: | :---: | :---: | :---: | :---: |
| **AdMob** | ❌ | ✅ | ✅ | ✅ | ❌ | | **AdMob** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Analytics**             | ✅ | ✅ | ✅ | ✅ | ❌ | | **Analytics**             | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ | | **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ | | **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ❌ | | _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ |
| **Core** | ❌ |**?**| ✅ | ✅ | ✅ | | **Core** | ❌ |**?**| ✅ | ✅ | ✅ | ✅ |
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ | | _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ |**?**| | **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ | ✅ |**?**|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ |**?**| | **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ❌ | | **Crashlytics**           | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ❌ | | **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ |**?**| ❌ | | **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ❌ | | **Invites** | ❌ | ❌ | ❌ |**?**|**?**| ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ | | **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ |**?**| | **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ | | _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| **Remote Config** | ✅ | ✅ | ✅ | ✅ | ❌ | | _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Storage** | ✅ | ✅ | ✅ | ✅ |**?**| | **Remote Config** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Storage** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
--- ---
### Supported versions - React Native / Firebase ### 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` > 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 | | | 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 + | | 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.4.2 + | | 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 + | | 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' ext.firebaseVersion = '11.4.2'
repositories { repositories {
jcenter() jcenter()
maven {
url 'https://maven.fabric.io/public'
}
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.3' 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-ads:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion" compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$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; return AdSize.LEADERBOARD;
case "SMART_BANNER": case "SMART_BANNER":
return AdSize.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.FirebaseApp;
import com.google.firebase.FirebaseException; import com.google.firebase.FirebaseException;
import com.google.firebase.auth.ActionCodeResult; import com.google.firebase.auth.ActionCodeResult;
import com.google.firebase.auth.ActionCodeSettings;
import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
@ -321,25 +322,32 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
* @param promise * @param promise
*/ */
@ReactMethod @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"); Log.d(TAG, "sendPasswordResetEmail");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.sendPasswordResetEmail(email) OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
.addOnCompleteListener(new OnCompleteListener<Void>() { @Override
@Override public void onComplete(@NonNull Task<Void> task) {
public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) {
if (task.isSuccessful()) { Log.d(TAG, "sendPasswordResetEmail:onComplete:success");
Log.d(TAG, "sendPasswordResetEmail:onComplete:success"); promiseNoUser(promise, false);
promiseNoUser(promise, false); } else {
} else { Exception exception = task.getException();
Exception exception = task.getException(); Log.e(TAG, "sendPasswordResetEmail:onComplete:failure", exception);
Log.e(TAG, "sendPasswordResetEmail:onComplete:failure", exception); promiseRejectAuthException(promise, 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 * @param promise
*/ */
@ReactMethod @ReactMethod
public void sendEmailVerification(String appName, final Promise promise) { public void sendEmailVerification(String appName, ReadableMap actionCodeSettings, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -450,20 +458,26 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
promiseNoUser(promise, false); promiseNoUser(promise, false);
Log.e(TAG, "sendEmailVerification:failure:noCurrentUser"); Log.e(TAG, "sendEmailVerification:failure:noCurrentUser");
} else { } else {
user.sendEmailVerification() OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
.addOnCompleteListener(new OnCompleteListener<Void>() { @Override
@Override public void onComplete(@NonNull Task<Void> task) {
public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) {
if (task.isSuccessful()) { Log.d(TAG, "sendEmailVerification:onComplete:success");
Log.d(TAG, "sendEmailVerification:onComplete:success"); promiseWithUser(firebaseAuth.getCurrentUser(), promise);
promiseWithUser(firebaseAuth.getCurrentUser(), promise); } else {
} else { Exception exception = task.getException();
Exception exception = task.getException(); Log.e(TAG, "sendEmailVerification:onComplete:failure", exception);
Log.e(TAG, "sendEmailVerification:onComplete:failure", exception); promiseRejectAuthException(promise, 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; 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 appName
* @param requestKey * @param requestKey

View File

@ -1,6 +1,7 @@
package io.invertase.firebase.database; package io.invertase.firebase.database;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
@ -13,6 +14,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseApp;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.Logger; import com.google.firebase.database.Logger;
import com.google.firebase.database.MutableData; import com.google.firebase.database.MutableData;
import com.google.firebase.database.OnDisconnect; import com.google.firebase.database.OnDisconnect;
@ -35,6 +37,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseDatabase"; private static final String TAG = "RNFirebaseDatabase";
private boolean enableLogging = false; private boolean enableLogging = false;
private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>(); private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
private HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>(); private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseDatabase(ReactApplicationContext reactContext) { RNFirebaseDatabase(ReactApplicationContext reactContext) {
@ -89,10 +92,19 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
enableLogging = enabled; enableLogging = enabled;
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext()); List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
for (FirebaseApp app : firebaseAppList) { for (FirebaseApp app : firebaseAppList) {
if (enableLogging) { loggingLevelSet.put(app.getName(), enabled);
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.DEBUG); try {
} else { if (enableLogging) {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.WARN); 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) { private FirebaseDatabase getDatabaseForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp); 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 && (logLevel == null || !logLevel)) {
// if (enableLogging) { try {
// firebaseDatabase.setLogLevel(Logger.Level.DEBUG); loggingLevelSet.put(firebaseDatabase.getApp().getName(), enableLogging);
// } else { firebaseDatabase.setLogLevel(Logger.Level.DEBUG);
// 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());
}
} 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; 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.ReactMethod;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Task;
@ -27,7 +26,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import io.invertase.firebase.ErrorUtils; import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestore extends ReactContextBaseJavaModule { public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@ -42,6 +40,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/* /*
* REACT NATIVE METHODS * REACT NATIVE METHODS
*/ */
/**
* @param enabled
*/
@ReactMethod
public void enableLogging(Boolean enabled) {
FirebaseFirestore.setLoggingEnabled(enabled);
}
@ReactMethod @ReactMethod
public void collectionGet(String appName, String path, ReadableArray filters, public void collectionGet(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, final Promise promise) { ReadableArray orders, ReadableMap options, final Promise promise) {

View File

@ -104,7 +104,13 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L
@ReactMethod @ReactMethod
public void cancelAllLocalNotifications() { 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 @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' require 'json'
package = JSON.parse(File.read('package.json')) package = JSON.parse(File.read('../package.json'))
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "RNFirebase" s.name = "RNFirebase"
@ -12,9 +12,8 @@ Pod::Spec.new do |s|
s.license = package['license'] s.license = package['license']
s.authors = "Invertase Limited" s.authors = "Invertase Limited"
s.source = { :git => "https://github.com/invertase/react-native-firebase.git", :tag => "v#{s.version}" } s.source = { :git => "https://github.com/invertase/react-native-firebase.git", :tag => "v#{s.version}" }
s.social_media_url = 'http://twitter.com/mikediarmid' s.social_media_url = 'http://twitter.com/RNFirebase'
s.platform = :ios, "8.0" s.platform = :ios, "9.0"
s.preserve_paths = 'README.md', 'package.json', '*.js' s.source_files = 'RNFirebase/**/*.{h,m}'
s.source_files = 'ios/RNFirebase/**/*.{h,m}'
s.dependency 'React' s.dependency 'React'
end end

View File

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

View File

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

View File

@ -3,11 +3,15 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <React/RCTEventEmitter.h> #import <React/RCTEventEmitter.h>
#import <Firebase.h>
@interface RNFirebaseUtil : NSObject @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)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 @end

View File

@ -2,6 +2,28 @@
@implementation RNFirebaseUtil @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 { + (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body {
@try { @try {
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233 // 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 // Add the appName to the body
NSMutableDictionary *newBody = [body mutableCopy]; NSMutableDictionary *newBody = [body mutableCopy];
newBody[@"appName"] = appName; newBody[@"appName"] = [RNFirebaseUtil getAppDisplayName:app.name];
[RNFirebaseUtil sendJSEvent:emitter name:name body:newBody]; [RNFirebaseUtil sendJSEvent:emitter name:name body:newBody];
} }

View File

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

View File

@ -23,19 +23,19 @@ RCT_EXPORT_MODULE();
*/ */
RCT_EXPORT_METHOD(addAuthStateListener: RCT_EXPORT_METHOD(addAuthStateListener:
(NSString *) appName) { (NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_authStateHandlers valueForKey:appName]) { if (![_authStateHandlers valueForKey:firApp.name]) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) { 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 { } 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: RCT_EXPORT_METHOD(removeAuthStateListener:
(NSString *) appName) { (NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([_authStateHandlers valueForKey:appName]) { if ([_authStateHandlers valueForKey:firApp.name]) {
FIRApp *firApp = [FIRApp appNamed:appName]; [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:firApp.name]];
[[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:appName]]; [_authStateHandlers removeObjectForKey:firApp.name];
[_authStateHandlers removeObjectForKey:appName];
} }
} }
@ -58,19 +58,19 @@ RCT_EXPORT_METHOD(removeAuthStateListener:
*/ */
RCT_EXPORT_METHOD(addIdTokenListener: RCT_EXPORT_METHOD(addIdTokenListener:
(NSString *) appName) { (NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_idTokenHandlers valueForKey:appName]) { if (![_idTokenHandlers valueForKey:firApp.name]) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) { FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) { 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 { } 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: RCT_EXPORT_METHOD(removeIdTokenListener:
(NSString *) appName) { (NSString *) appDisplayName) {
if ([_idTokenHandlers valueForKey:appName]) { FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:appName]]; if ([_idTokenHandlers valueForKey:firApp.name]) {
[_idTokenHandlers removeObjectForKey:appName]; [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:firApp.name]];
[_idTokenHandlers removeObjectForKey:firApp.name];
} }
} }
@ -97,12 +98,12 @@ RCT_EXPORT_METHOD(removeIdTokenListener:
@return @return
*/ */
RCT_EXPORT_METHOD(signOut: RCT_EXPORT_METHOD(signOut:
(NSString *) appName (NSString *) appDisplayName
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -125,12 +126,12 @@ RCT_EXPORT_METHOD(signOut:
@return @return
*/ */
RCT_EXPORT_METHOD(signInAnonymously: RCT_EXPORT_METHOD(signInAnonymously:
(NSString *) appName (NSString *) appDisplayName
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
if (error) { if (error) {
@ -152,7 +153,7 @@ RCT_EXPORT_METHOD(signInAnonymously:
@return return @return return
*/ */
RCT_EXPORT_METHOD(signInWithEmailAndPassword: RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(NSString *) appName (NSString *) appDisplayName
email: email:
(NSString *) email (NSString *) email
pass: pass:
@ -161,7 +162,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) { if (error) {
@ -182,7 +183,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
@return return @return return
*/ */
RCT_EXPORT_METHOD(createUserWithEmailAndPassword: RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(NSString *) appName (NSString *) appDisplayName
email: email:
(NSString *) email (NSString *) email
pass: pass:
@ -191,7 +192,7 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) { if (error) {
@ -210,12 +211,12 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
@return return @return return
*/ */
RCT_EXPORT_METHOD(delete: RCT_EXPORT_METHOD(delete:
(NSString *) appName (NSString *) appDisplayName
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) { if (user) {
@ -239,12 +240,12 @@ RCT_EXPORT_METHOD(delete:
@return return @return return
*/ */
RCT_EXPORT_METHOD(reload: RCT_EXPORT_METHOD(reload:
(NSString *) appName (NSString *) appDisplayName
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -262,24 +263,28 @@ RCT_EXPORT_METHOD(reload:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return return @return return
*/ */
RCT_EXPORT_METHOD(sendEmailVerification: RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appDisplayName
(NSString *) appName actionCodeSettings:(NSDictionary *) actionCodeSettings
resolver: resolver:(RCTPromiseResolveBlock) resolve
(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
rejecter: FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) { if (user) {
[user sendEmailVerificationWithCompletion:^(NSError *_Nullable error) { id handler = ^(NSError *_Nullable error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error:error]; [self promiseRejectAuthException:reject error:error];
} else { } else {
FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser; FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:userAfterUpdate]; [self promiseWithUser:resolve rejecter:reject user:userAfterUpdate];
} }
}]; };
if (actionCodeSettings) {
FIRActionCodeSettings *settings = [self buildActionCodeSettings:actionCodeSettings];
[user sendEmailVerificationWithActionCodeSettings:settings completion:handler];
} else {
[user sendEmailVerificationWithCompletion:handler];
}
} else { } else {
[self promiseNoUser:resolve rejecter:reject isError:YES]; [self promiseNoUser:resolve rejecter:reject isError:YES];
} }
@ -294,14 +299,14 @@ RCT_EXPORT_METHOD(sendEmailVerification:
@return return @return return
*/ */
RCT_EXPORT_METHOD(updateEmail: RCT_EXPORT_METHOD(updateEmail:
(NSString *) appName (NSString *) appDisplayName
email: email:
(NSString *) email (NSString *) email
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) { if (user) {
@ -326,14 +331,14 @@ RCT_EXPORT_METHOD(updateEmail:
@return return @return return
*/ */
RCT_EXPORT_METHOD(updatePassword: RCT_EXPORT_METHOD(updatePassword:
(NSString *) appName (NSString *) appDisplayName
password: password:
(NSString *) password (NSString *) password
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -360,14 +365,14 @@ RCT_EXPORT_METHOD(updatePassword:
@return return @return return
*/ */
RCT_EXPORT_METHOD(updateProfile: RCT_EXPORT_METHOD(updateProfile:
(NSString *) appName (NSString *) appDisplayName
props: props:
(NSDictionary *) props (NSDictionary *) props
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -408,14 +413,14 @@ RCT_EXPORT_METHOD(updateProfile:
@return @return
*/ */
RCT_EXPORT_METHOD(getToken: RCT_EXPORT_METHOD(getToken:
(NSString *) appName (NSString *) appDisplayName
forceRefresh: forceRefresh:
(BOOL) forceRefresh (BOOL) forceRefresh
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -443,7 +448,7 @@ RCT_EXPORT_METHOD(getToken:
@return @return
*/ */
RCT_EXPORT_METHOD(signInWithCredential: RCT_EXPORT_METHOD(signInWithCredential:
(NSString *) appName (NSString *) appDisplayName
provider: provider:
(NSString *) provider (NSString *) provider
token: token:
@ -454,7 +459,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -481,7 +486,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
@return @return
*/ */
RCT_EXPORT_METHOD(confirmPasswordReset: RCT_EXPORT_METHOD(confirmPasswordReset:
(NSString *) appName (NSString *) appDisplayName
code: code:
(NSString *) code (NSString *) code
newPassword: newPassword:
@ -490,8 +495,8 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] confirmPasswordResetWithCode:code newPassword:newPassword completion:^(NSError *_Nullable error) { [[FIRAuth authWithApp:firApp] confirmPasswordResetWithCode:code newPassword:newPassword completion:^(NSError *_Nullable error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error:error]; [self promiseRejectAuthException:reject error:error];
@ -511,15 +516,15 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
* @return * @return
*/ */
RCT_EXPORT_METHOD(applyActionCode: RCT_EXPORT_METHOD(applyActionCode:
(NSString *) appName (NSString *) appDisplayName
code: code:
(NSString *) code (NSString *) code
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] applyActionCode:code completion:^(NSError *_Nullable error) { [[FIRAuth authWithApp:firApp] applyActionCode:code completion:^(NSError *_Nullable error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error:error]; [self promiseRejectAuthException:reject error:error];
@ -538,15 +543,15 @@ RCT_EXPORT_METHOD(applyActionCode:
* @return * @return
*/ */
RCT_EXPORT_METHOD(checkActionCode: RCT_EXPORT_METHOD(checkActionCode:
(NSString *) appName (NSString *) appDisplayName
code: code:
(NSString *) code (NSString *) code
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) { [[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error:error]; [self promiseRejectAuthException:reject error:error];
@ -582,23 +587,27 @@ RCT_EXPORT_METHOD(checkActionCode:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(sendPasswordResetEmail: RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
(NSString *) appName email:(NSString *) email
email: actionCodeSettings:(NSDictionary *) actionCodeSettings
(NSString *) email resolver:(RCTPromiseResolveBlock) resolve
resolver: rejecter:(RCTPromiseRejectBlock) reject) {
(RCTPromiseResolveBlock) resolve FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] sendPasswordResetWithEmail:email completion:^(NSError *_Nullable error) { id handler = ^(NSError *_Nullable error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error:error]; [self promiseRejectAuthException:reject error:error];
} else { } else {
[self promiseNoUser:resolve rejecter:reject isError:NO]; [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 @return
*/ */
RCT_EXPORT_METHOD(getCurrentUser: RCT_EXPORT_METHOD(getCurrentUser:
(NSString *) appName (NSString *) appDisplayName
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:user]; [self promiseWithUser:resolve rejecter:reject user:user];
@ -629,14 +638,14 @@ RCT_EXPORT_METHOD(getCurrentUser:
@return @return
*/ */
RCT_EXPORT_METHOD(signInWithCustomToken: RCT_EXPORT_METHOD(signInWithCustomToken:
(NSString *) appName (NSString *) appDisplayName
customToken: customToken:
(NSString *) customToken (NSString *) customToken
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) {
if (error) { if (error) {
@ -655,11 +664,11 @@ RCT_EXPORT_METHOD(signInWithCustomToken:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber phoneNumber:(NSString *) phoneNumber
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { 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) { [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) { if (error) {
@ -676,17 +685,17 @@ RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
/** /**
verifyPhoneNumber verifyPhoneNumber
@param string phoneNumber @param string phoneNumber
@param RCTPromiseResolveBlock resolve @param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber phoneNumber:(NSString *) phoneNumber
requestKey:(NSString *) requestKey) { 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) { [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) { if (error) {
NSDictionary * jsError = [self getJSError:(error)]; NSDictionary * jsError = [self getJSError:(error)];
@ -695,7 +704,7 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey, @"requestKey":requestKey,
@"state": @{@"error": jsError}, @"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 { } else {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"]; [defaults setObject:verificationID forKey:@"authVerificationID"];
@ -704,16 +713,16 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey, @"requestKey":requestKey,
@"state": @{@"verificationId": verificationID}, @"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 verificationCode:(NSString *) verificationCode
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *verificationId = [defaults stringForKey:@"authVerificationID"]; NSString *verificationId = [defaults stringForKey:@"authVerificationID"];
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode]; FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode];
@ -738,7 +747,7 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
@return @return
*/ */
RCT_EXPORT_METHOD(link: RCT_EXPORT_METHOD(link:
(NSString *) appName (NSString *) appDisplayName
provider: provider:
(NSString *) provider (NSString *) provider
authToken: authToken:
@ -749,7 +758,7 @@ RCT_EXPORT_METHOD(link:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -783,14 +792,14 @@ RCT_EXPORT_METHOD(link:
@return @return
*/ */
RCT_EXPORT_METHOD(unlink: RCT_EXPORT_METHOD(unlink:
(NSString *) appName (NSString *) appDisplayName
providerId: providerId:
(NSString *) providerId (NSString *) providerId
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) { if (user) {
@ -817,7 +826,7 @@ RCT_EXPORT_METHOD(unlink:
@return @return
*/ */
RCT_EXPORT_METHOD(reauthenticate: RCT_EXPORT_METHOD(reauthenticate:
(NSString *) appName (NSString *) appDisplayName
provider: provider:
(NSString *) provider (NSString *) provider
authToken: authToken:
@ -828,7 +837,7 @@ RCT_EXPORT_METHOD(reauthenticate:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -861,14 +870,14 @@ RCT_EXPORT_METHOD(reauthenticate:
@return @return
*/ */
RCT_EXPORT_METHOD(fetchProvidersForEmail: RCT_EXPORT_METHOD(fetchProvidersForEmail:
(NSString *) appName (NSString *) appDisplayName
email: email:
(NSString *) email (NSString *) email
resolver: resolver:
(RCTPromiseResolveBlock) resolve (RCTPromiseResolveBlock) resolve
rejecter: rejecter:
(RCTPromiseRejectBlock) reject) { (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) { [[FIRAuth authWithApp:firApp] fetchProvidersForEmail:email completion:^(NSArray<NSString *> *_Nullable providers, NSError *_Nullable error) {
if (error) { if (error) {
@ -954,14 +963,14 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
/** /**
Reject a promise with an auth exception Reject a promise with an auth exception
@param error NSError @param error NSError
*/ */
- (NSDictionary *)getJSError:(NSError *)error { - (NSDictionary *)getJSError:(NSError *)error {
NSString *code = @"auth/unknown"; NSString *code = @"auth/unknown";
NSString *message = [error localizedDescription]; NSString *message = [error localizedDescription];
NSString *nativeErrorMessage = [error localizedDescription]; NSString *nativeErrorMessage = [error localizedDescription];
switch (error.code) { switch (error.code) {
case FIRAuthErrorCodeInvalidCustomToken: case FIRAuthErrorCodeInvalidCustomToken:
code = @"auth/invalid-custom-token"; code = @"auth/invalid-custom-token";
@ -1035,7 +1044,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
code = @"auth/internal-error"; code = @"auth/internal-error";
message = @"An internal error has occurred, please try again."; message = @"An internal error has occurred, please try again.";
break; break;
// unsure of the below codes so leaving them as the default error message // unsure of the below codes so leaving them as the default error message
case FIRAuthErrorCodeTooManyRequests: case FIRAuthErrorCodeTooManyRequests:
code = @"auth/too-many-requests"; code = @"auth/too-many-requests";
@ -1070,7 +1079,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
default: default:
break; break;
} }
return @{ return @{
@"code": code, @"code": code,
@"message": message, @"message": message,
@ -1154,6 +1163,30 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
return userDict; 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 { - (NSArray<NSString *> *)supportedEvents {
return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT]; 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; + (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject databaseError:(NSError *)databaseError;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appName; + (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError; + (NSDictionary *)getJSError:(NSError *)nativeError;

View File

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

View File

@ -12,12 +12,12 @@
@interface RNFirebaseDatabaseReference : NSObject @interface RNFirebaseDatabaseReference : NSObject
@property RCTEventEmitter *emitter; @property RCTEventEmitter *emitter;
@property FIRDatabaseQuery *query; @property FIRDatabaseQuery *query;
@property NSString *app; @property NSString *appDisplayName;
@property NSString *key; @property NSString *key;
@property NSString *path; @property NSString *path;
@property NSMutableDictionary *listeners; @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)on:(NSString *) eventName registration:(NSDictionary *) registration;
- (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; - (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)removeEventListener:(NSString *)eventRegistrationKey; - (void)removeEventListener:(NSString *)eventRegistrationKey;

View File

@ -5,14 +5,14 @@
#if __has_include(<FirebaseDatabase/FIRDatabase.h>) #if __has_include(<FirebaseDatabase/FIRDatabase.h>)
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter - (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
app:(NSString *) app appDisplayName:(NSString *) appDisplayName
key:(NSString *) key key:(NSString *) key
refPath:(NSString *) refPath refPath:(NSString *) refPath
modifiers:(NSArray *) modifiers { modifiers:(NSArray *) modifiers {
self = [super init]; self = [super init];
if (self) { if (self) {
_emitter = emitter; _emitter = emitter;
_app = app; _appDisplayName = appDisplayName;
_key = key; _key = key;
_path = refPath; _path = refPath;
_listeners = [[NSMutableDictionary alloc] init]; _listeners = [[NSMutableDictionary alloc] init];
@ -123,7 +123,7 @@
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *) path - (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *) path
modifiers:(NSArray *)modifiers { modifiers:(NSArray *)modifiers {
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_app]; FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName];
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path]; FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
for (NSDictionary *modifier in modifiers) { 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; + (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName; + (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError; + (NSDictionary *)getJSError:(NSError *)nativeError;
@end @end

View File

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

View File

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

View File

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

View File

@ -13,11 +13,11 @@
@interface RNFirebaseFirestoreDocumentReference : NSObject @interface RNFirebaseFirestoreDocumentReference : NSObject
@property RCTEventEmitter *emitter; @property RCTEventEmitter *emitter;
@property NSString *app; @property NSString *appDisplayName;
@property NSString *path; @property NSString *path;
@property FIRDocumentReference *ref; @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)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; - (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId; + (void)offSnapshot:(NSString *)listenerId;

View File

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

View File

@ -23,11 +23,11 @@ RCT_EXPORT_MODULE(RNFirebaseStorage);
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
@param NSString path @param NSString path
*/ */
RCT_EXPORT_METHOD(delete:(NSString *) appName RCT_EXPORT_METHOD(delete:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName]; FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef deleteWithCompletion:^(NSError *_Nullable error) { [fileRef deleteWithCompletion:^(NSError *_Nullable error) {
if (error != nil) { 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 @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
@param NSString path @param NSString path
*/ */
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { 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) { [fileRef downloadURLWithCompletion:^(NSURL *_Nullable URL, NSError *_Nullable error) {
if (error != nil) { 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 @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
@param NSString path @param NSString path
*/ */
RCT_EXPORT_METHOD(getMetadata:(NSString *) appName RCT_EXPORT_METHOD(getMetadata:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { 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) { [fileRef metadataWithCompletion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
if (error != nil) { if (error != nil) {
@ -87,12 +87,12 @@ RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
@param NSString path @param NSString path
@param NSDictionary metadata @param NSDictionary metadata
*/ */
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName RCT_EXPORT_METHOD(updateMetadata:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
metadata:(NSDictionary *) metadata metadata:(NSDictionary *) metadata
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName]; FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata]; FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
[fileRef updateMetadata:firmetadata completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) { [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 path
@param NSString localPath @param NSString localPath
*/ */
RCT_EXPORT_METHOD(downloadFile:(NSString *) appName RCT_EXPORT_METHOD(downloadFile:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
localPath:(NSString *) localPath localPath:(NSString *) localPath
resolver:(RCTPromiseResolveBlock) resolve resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) { rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName]; FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
NSURL *localFile = [NSURL fileURLWithPath:localPath]; NSURL *localFile = [NSURL fileURLWithPath:localPath];
FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile]; FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile];
@ -124,25 +124,25 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
[downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) { [downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// download resumed, also fires when the upload starts // download resumed, also fires when the upload starts
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot]; 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) { [downloadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// download paused // download paused
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot]; 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) { [downloadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// download reported progress // download reported progress
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot]; 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) { [downloadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// download completed successfully // download completed successfully
NSDictionary *resp = [self getDownloadTaskAsDictionary:snapshot]; 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); resolve(resp);
}]; }];
@ -161,9 +161,10 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
@param NSNumber milliseconds @param NSNumber milliseconds
*/ */
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) { milliseconds:(nonnull NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxDownloadRetryTime:[milliseconds doubleValue]]; 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 @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
@param NSNumber milliseconds @param NSNumber milliseconds
*/ */
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) { milliseconds:(nonnull NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxOperationRetryTime:[milliseconds doubleValue]]; 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 @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
*/ */
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) { milliseconds:(nonnull NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxUploadRetryTime:[milliseconds doubleValue]]; 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 NSString localPath
@param NSDictionary metadata @param NSDictionary metadata
*/ */
RCT_EXPORT_METHOD(putFile:(NSString *) appName RCT_EXPORT_METHOD(putFile:(NSString *) appDisplayName
path:(NSString *) path path:(NSString *) path
localPath:(NSString *) localPath localPath:(NSString *) localPath
metadata:(NSDictionary *) metadata metadata:(NSDictionary *) metadata
@ -224,7 +227,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
if (info[PHImageErrorKey] == nil) { if (info[PHImageErrorKey] == nil) {
if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) { if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) {
firmetadata.contentType = [self utiToMimeType:dataUTI]; 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 { } else {
// if the image UTI is not JPEG then convert to JPEG, e.g. HEI // if the image UTI is not JPEG then convert to JPEG, e.g. HEI
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
@ -236,7 +239,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
CGImageDestinationFinalize(destination); CGImageDestinationFinalize(destination);
// Manually set mimetype to JPEG // Manually set mimetype to JPEG
firmetadata.contentType = @"image/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 { } else {
reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil); 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:^{ [exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) { if (exportSession.status == AVAssetExportSessionStatusCompleted) {
firmetadata.contentType = [self utiToMimeType:exportSession.outputFileType]; 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? // 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 { } else {
reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil); reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil);
@ -274,7 +277,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
} else { } else {
// TODO: Content type for file? // TODO: Content type for file?
NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath]; 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); 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 { - (void)uploadFile:(NSString *)appDisplayName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName]; FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putFile:url metadata:firmetadata]; 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 { - (void)uploadData:(NSString *)appDisplayName data:(NSData *)data firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName]; FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putData:data metadata:firmetadata]; 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. // listen for state changes, errors, and completion of the upload.
[uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) { [uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload resumed, also fires when the upload starts // upload resumed, also fires when the upload starts
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot]; 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) { [uploadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload paused // upload paused
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot]; 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) { [uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload reported progress // upload reported progress
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot]; 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) { [uploadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload completed successfully // upload completed successfully
NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot]; NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp]; [self sendJSEvent:appDisplayName 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_UPLOAD_SUCCESS props:resp];
resolve(resp); resolve(resp);
}]; }];
@ -335,12 +338,13 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
} }
- (FIRStorageReference *)getReference:(NSString *)path - (FIRStorageReference *)getReference:(NSString *)path
appName:(NSString *)appName { appDisplayName:(NSString *)appDisplayName {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([path hasPrefix:@"url::"]) { if ([path hasPrefix:@"url::"]) {
NSString *url = [path substringFromIndex:5]; NSString *url = [path substringFromIndex:5];
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceForURL:url]; return [[FIRStorage storageForApp:firApp] referenceForURL:url];
} else { } 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]; 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]}; 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 { - (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": appName, @"path": path, @"body": 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; crash(): RNFirebase.crash.Crash;
static fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics;
};
apps: Array<string>; apps: Array<string>;
googleApiAvailability: RNFirebase.GoogleApiAvailabilityType; googleApiAvailability: RNFirebase.GoogleApiAvailabilityType;
@ -846,5 +850,50 @@ declare module "react-native-firebase" {
[key: string]: any; [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 { NativeModules, Platform } from 'react-native';
import { statics } from './'; import { statics } from './';
import AdRequest from './AdRequest'; import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils'; import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob; const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -23,8 +24,8 @@ export default class Interstitial {
this.admob = admob; this.admob = admob;
this.adUnit = adUnit; this.adUnit = adUnit;
this.loaded = false; this.loaded = false;
this.admob.removeAllListeners(`interstitial_${adUnit}`); SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
this.admob.on(`interstitial_${adUnit}`, this._onInterstitialEvent); SharedEventEmitter.addListener(`interstitial_${adUnit}`, this._onInterstitialEvent);
} }
/** /**
@ -48,8 +49,8 @@ export default class Interstitial {
default: default:
} }
this.admob.emit(eventType, emitData); SharedEventEmitter.emit(eventType, emitData);
this.admob.emit(`interstitial:${this.adUnit}:*`, emitData); SharedEventEmitter.emit(`interstitial:${this.adUnit}:*`, emitData);
}; };
/** /**
@ -97,7 +98,7 @@ export default class Interstitial {
return null; return null;
} }
const sub = this.admob.on(`interstitial:${this.adUnit}:${eventType}`, listenerCb); const sub = SharedEventEmitter.addListener(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
subscriptions.push(sub); subscriptions.push(sub);
return sub; return sub;
} }

View File

@ -1,6 +1,7 @@
import { NativeModules } from 'react-native'; import { NativeModules } from 'react-native';
import { statics } from './'; import { statics } from './';
import AdRequest from './AdRequest'; import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils'; import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob; const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -18,8 +19,8 @@ export default class RewardedVideo {
this.admob = admob; this.admob = admob;
this.adUnit = adUnit; this.adUnit = adUnit;
this.loaded = false; this.loaded = false;
this.admob.removeAllListeners(`rewarded_video_${adUnit}`); SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
this.admob.on(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent); SharedEventEmitter.addListener(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
} }
/** /**
@ -43,8 +44,8 @@ export default class RewardedVideo {
default: default:
} }
this.admob.emit(eventType, emitData); SharedEventEmitter.emit(eventType, emitData);
this.admob.emit(`rewarded_video:${this.adUnit}:*`, emitData); SharedEventEmitter.emit(`rewarded_video:${this.adUnit}:*`, emitData);
}; };
/** /**
@ -97,7 +98,7 @@ export default class RewardedVideo {
return null; 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); subscriptions.push(sub);
return 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 Interstitial from './Interstitial';
import RewardedVideo from './RewardedVideo'; import RewardedVideo from './RewardedVideo';
@ -12,72 +19,89 @@ import EventTypes, {
RewardedVideoEventTypes, RewardedVideoEventTypes,
} from './EventTypes'; } from './EventTypes';
export default class AdMob extends ModuleBase { import type App from '../core/firebase-app';
static _NAMESPACE = 'admob';
static _NATIVE_MODULE = 'RNFirebaseAdMob';
constructor(firebaseApp: Object, options: Object = {}) { type NativeEvent = {
super(firebaseApp, options, true); 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._initialized = false;
this._appId = null; this._appId = null;
this._eventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this)); SharedEventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
this._eventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this)); SharedEventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
} }
_onInterstitialEvent(event) { _onInterstitialEvent(event: NativeEvent): void {
const { adUnit } = event; const { adUnit } = event;
const jsEventType = `interstitial_${adUnit}`; const jsEventType = `interstitial_${adUnit}`;
if (!this.hasListeners(jsEventType)) { if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO // TODO
} }
this.emit(jsEventType, event); SharedEventEmitter.emit(jsEventType, event);
} }
_onRewardedVideoEvent(event) { _onRewardedVideoEvent(event: NativeEvent): void {
const { adUnit } = event; const { adUnit } = event;
const jsEventType = `rewarded_video_${adUnit}`; const jsEventType = `rewarded_video_${adUnit}`;
if (!this.hasListeners(jsEventType)) { if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO // TODO
} }
this.emit(jsEventType, event); SharedEventEmitter.emit(jsEventType, event);
} }
initialize(appId: string) { initialize(appId: string): void {
if (this._initialized) { if (this._initialized) {
this.log.warn('AdMob has already been initialized!'); getLogger(this).warn('AdMob has already been initialized!');
} else { } else {
this._initialized = true; this._initialized = true;
this._appId = appId; this._appId = appId;
this._native.initialize(appId); getNativeModule(this).initialize(appId);
} }
} }
openDebugMenu() { openDebugMenu(): void {
if (!this._initialized) { 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 { } else {
this.log.info('Opening debug menu'); getLogger(this).info('Opening debug menu');
this._native.openDebugMenu(this._appId); getNativeModule(this).openDebugMenu(this._appId);
} }
} }
interstitial(adUnit: string) { interstitial(adUnit: string): Interstitial {
return new Interstitial(this, adUnit); return new Interstitial(this, adUnit);
} }
rewarded(adUnit: string) { rewarded(adUnit: string): RewardedVideo {
return new RewardedVideo(this, adUnit); return new RewardedVideo(this, adUnit);
} }
get namespace(): string {
return 'firebase:admob';
}
} }
export const statics = { 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_]+$/; const AlphaNumericUnderscore = /^[a-zA-Z0-9_]+$/;
@ -19,12 +25,16 @@ const ReservedEventNames = [
'user_engagement', 'user_engagement',
]; ];
export default class Analytics extends ModuleBase { export const MODULE_NAME = 'RNFirebaseAnalytics';
static _NAMESPACE = 'analytics'; export const NAMESPACE = 'analytics';
static _NATIVE_MODULE = 'RNFirebaseAnalytics';
constructor(firebaseApp: Object, options: Object = {}) { export default class Analytics extends ModuleBase {
super(firebaseApp, options); 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_" // 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. // 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 * @param enabled
*/ */
setAnalyticsCollectionEnabled(enabled: boolean): void { 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 * @param screenClassOverride
*/ */
setCurrentScreen(screenName: string, screenClassOverride: string): void { 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 * @param milliseconds
*/ */
setMinimumSessionDuration(milliseconds: number = 10000): void { 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 * @param milliseconds
*/ */
setSessionTimeoutDuration(milliseconds: number = 1800000): void { 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 * @param id
*/ */
setUserId(id: string): void { 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 * @param value
*/ */
setUserProperty(name: string, value: string): void { 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 { setUserProperties(object: Object): void {
for (const property of Object.keys(object)) { 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 { export default class ConfirmationResult {
_auth: Object; _auth: Auth;
_verificationId: string; _verificationId: string;
/** /**
@ -10,7 +15,7 @@ export default class ConfirmationResult {
* @param auth * @param auth
* @param verificationId The phone number authentication operation's verification ID. * @param verificationId The phone number authentication operation's verification ID.
*/ */
constructor(auth: Object, verificationId: string) { constructor(auth: Auth, verificationId: string) {
this._auth = auth; this._auth = auth;
this._verificationId = verificationId; this._verificationId = verificationId;
} }
@ -20,13 +25,11 @@ export default class ConfirmationResult {
* @param verificationCode * @param verificationCode
* @return {*} * @return {*}
*/ */
confirm(verificationCode: string): Promise<Object> { confirm(verificationCode: string): Promise<?User> {
return this._auth._interceptUserValue( return this._auth._interceptUserValue(getNativeModule(this._auth)._confirmVerificationCode(verificationCode));
this._auth._native._confirmVerificationCode(verificationCode)
);
} }
get verificationId(): String | null { get verificationId(): string | null {
return this._verificationId; return this._verificationId;
} }
} }

View File

@ -1,6 +1,10 @@
// @flow // @flow
import INTERNALS from './../../internals'; import INTERNALS from '../../utils/internals';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from './../../utils'; 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 = { type PhoneAuthSnapshot = {
state: 'sent' | 'timeout' | 'verified' | 'error', state: 'sent' | 'timeout' | 'verified' | 'error',
@ -17,8 +21,7 @@ type PhoneAuthError = {
}; };
export default class PhoneAuthListener { export default class PhoneAuthListener {
_auth: Auth;
_auth: Object;
_timeout: number; _timeout: number;
_publicEvents: Object; _publicEvents: Object;
_internalEvents: Object; _internalEvents: Object;
@ -34,7 +37,7 @@ export default class PhoneAuthListener {
* @param phoneNumber * @param phoneNumber
* @param timeout * @param timeout
*/ */
constructor(auth: Object, phoneNumber: string, timeout?: number) { constructor(auth: Auth, phoneNumber: string, timeout?: number) {
this._auth = auth; this._auth = auth;
this._reject = null; this._reject = null;
this._resolve = null; this._resolve = null;
@ -67,7 +70,7 @@ export default class PhoneAuthListener {
// start verification flow natively // start verification flow natively
if (isAndroid) { if (isAndroid) {
this._auth._native.verifyPhoneNumber( getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber, phoneNumber,
this._phoneAuthRequestKey, this._phoneAuthRequestKey,
this._timeout, this._timeout,
@ -75,7 +78,7 @@ export default class PhoneAuthListener {
} }
if (isIOS) { if (isIOS) {
this._auth._native.verifyPhoneNumber( getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber, phoneNumber,
this._phoneAuthRequestKey, this._phoneAuthRequestKey,
); );
@ -91,7 +94,8 @@ export default class PhoneAuthListener {
for (let i = 0, len = events.length; i < len; i++) { for (let i = 0, len = events.length; i < len; i++) {
const type = events[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 * @private
*/ */
_addUserObserver(observer) { _addUserObserver(observer) {
this._auth.on(this._publicEvents.event, observer); SharedEventEmitter.addListener(this._publicEvents.event, observer);
} }
/** /**
@ -110,7 +114,7 @@ export default class PhoneAuthListener {
* @private * @private
*/ */
_emitToObservers(snapshot: PhoneAuthSnapshot) { _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 * @private
*/ */
_emitToErrorCb(snapshot) { _emitToErrorCb(snapshot) {
const error = snapshot.error; const { error } = snapshot;
if (this._reject) this._reject(error); 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) { _emitToSuccessCb(snapshot) {
if (this._resolve) this._resolve(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 setTimeout(() => { // move to next event loop - not sure if needed
// internal listeners // internal listeners
Object.values(this._internalEvents).forEach((event) => { Object.values(this._internalEvents).forEach((event) => {
this._auth.removeAllListeners(event); SharedEventEmitter.removeAllListeners(event);
}); });
// user observer listeners // user observer listeners
Object.values(this._publicEvents).forEach((publicEvent) => { Object.values(this._publicEvents).forEach((publicEvent) => {
this._auth.removeAllListeners(publicEvent); SharedEventEmitter.removeAllListeners(publicEvent);
}); });
}, 0); }, 0);
} }
@ -278,11 +282,11 @@ export default class PhoneAuthListener {
this._addUserObserver(observer); this._addUserObserver(observer);
if (isFunction(errorCb)) { if (isFunction(errorCb)) {
this._auth.once(this._publicEvents.error, errorCb); SharedEventEmitter.once(this._publicEvents.error, errorCb);
} }
if (isFunction(successCb)) { if (isFunction(successCb)) {
this._auth.once(this._publicEvents.success, successCb); SharedEventEmitter.once(this._publicEvents.success, successCb);
} }
return this; return this;
@ -294,6 +298,7 @@ export default class PhoneAuthListener {
*/ */
then(fn: () => PhoneAuthSnapshot) { then(fn: () => PhoneAuthSnapshot) {
this._promiseDeferred(); this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.then.bind(this._promise)(fn); if (this._promise) return this._promise.then.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy return undefined; // will never get here - just to keep flow happy
} }
@ -304,6 +309,7 @@ export default class PhoneAuthListener {
*/ */
catch(fn: () => Error) { catch(fn: () => Error) {
this._promiseDeferred(); this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.catch.bind(this._promise)(fn); if (this._promise) return this._promise.catch.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy 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'; * @flow
import ModuleBase from './../../utils/ModuleBase'; * Auth representation wrapper
import INTERNALS from './../../internals'; */
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'; import ConfirmationResult from './ConfirmationResult';
// providers // providers
@ -14,43 +20,59 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener'; 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 { export default class Auth extends ModuleBase {
static _NAMESPACE = 'auth'; _authResult: AuthResult | null;
static _NATIVE_MODULE = 'RNFirebaseAuth';
_user: User | null; _user: User | null;
_native: Object;
_getAppEventName: Function;
_authResult: AuthResultType | null;
constructor(firebaseApp: Object, options: Object = {}) { constructor(app: App) {
super(firebaseApp, options, true); super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._user = null; this._user = null;
this._authResult = null; this._authResult = null;
this.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onAuthStateChanged // public event name: onAuthStateChanged
this._getAppEventName('auth_state_changed'), getAppEventName(this, 'auth_state_changed'),
this._onInternalAuthStateChanged.bind(this), this._onInternalAuthStateChanged.bind(this),
); );
this.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public events based on event.type // public events based on event.type
this._getAppEventName('phone_auth_state_changed'), getAppEventName(this, 'phone_auth_state_changed'),
this._onInternalPhoneAuthStateChanged.bind(this), this._onInternalPhoneAuthStateChanged.bind(this),
); );
this.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onIdTokenChanged // public event name: onIdTokenChanged
this._getAppEventName('auth_id_token_changed'), getAppEventName(this, 'auth_id_token_changed'),
this._onInternalIdTokenChanged.bind(this), this._onInternalIdTokenChanged.bind(this),
); );
this._native.addAuthStateListener(); getNativeModule(this).addAuthStateListener();
this._native.addIdTokenListener(); getNativeModule(this).addIdTokenListener();
} }
/** /**
@ -60,13 +82,13 @@ export default class Auth extends ModuleBase {
*/ */
_onInternalPhoneAuthStateChanged(event: Object) { _onInternalPhoneAuthStateChanged(event: Object) {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`; 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._authResult = auth;
this._user = auth && auth.user ? new User(this, auth.user) : null; 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 * @param auth
* @private * @private
*/ */
_onInternalAuthStateChanged(auth: AuthResultType) { _onInternalAuthStateChanged(auth: AuthResult) {
this._setAuthState(auth); 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 * @param emit
* @private * @private
*/ */
_onInternalIdTokenChanged(auth: AuthResultType) { _onInternalIdTokenChanged(auth: AuthResult) {
this._setAuthState(auth); 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.<*>} * @returns {Promise.<*>}
* @private * @private
*/ */
_interceptUserValue(promise) { _interceptUserValue(promise: Promise<AuthResult>): Promise<User> {
return promise.then((result) => { return promise.then((result: AuthResult) => {
if (!result) this._setAuthState(null); if (!result) this._setAuthState(null);
else if (result.user) this._setAuthState(result); else if (result.user) this._setAuthState(result);
else if (result.uid) this._setAuthState({ authenticated: true, user: 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 * WEB API
*/ */
@ -115,8 +142,8 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
onAuthStateChanged(listener: Function) { onAuthStateChanged(listener: Function) {
this.log.info('Creating onAuthStateChanged listener'); getLogger(this).info('Creating onAuthStateChanged listener');
this.on(this._getAppEventName('onAuthStateChanged'), listener); SharedEventEmitter.addListener(getAppEventName(this, 'onAuthStateChanged'), listener);
if (this._authResult) listener(this._user || null); if (this._authResult) listener(this._user || null);
return this._offAuthStateChanged.bind(this, listener); return this._offAuthStateChanged.bind(this, listener);
} }
@ -126,8 +153,8 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
_offAuthStateChanged(listener: Function) { _offAuthStateChanged(listener: Function) {
this.log.info('Removing onAuthStateChanged listener'); getLogger(this).info('Removing onAuthStateChanged listener');
this.removeListener(this._getAppEventName('onAuthStateChanged'), listener); SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener);
} }
/** /**
@ -135,8 +162,8 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
onIdTokenChanged(listener: Function) { onIdTokenChanged(listener: Function) {
this.log.info('Creating onIdTokenChanged listener'); getLogger(this).info('Creating onIdTokenChanged listener');
this.on(this._getAppEventName('onIdTokenChanged'), listener); SharedEventEmitter.addListener(getAppEventName(this, 'onIdTokenChanged'), listener);
if (this._authResult) listener(this._user || null); if (this._authResult) listener(this._user || null);
return this._offIdTokenChanged.bind(this, listener); return this._offIdTokenChanged.bind(this, listener);
} }
@ -146,8 +173,8 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
_offIdTokenChanged(listener: Function) { _offIdTokenChanged(listener: Function) {
this.log.info('Removing onIdTokenChanged listener'); getLogger(this).info('Removing onIdTokenChanged listener');
this.removeListener(this._getAppEventName('onIdTokenChanged'), listener); SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener);
} }
/** /**
@ -155,8 +182,8 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
onUserChanged(listener: Function) { onUserChanged(listener: Function) {
this.log.info('Creating onUserChanged listener'); getLogger(this).info('Creating onUserChanged listener');
this.on(this._getAppEventName('onUserChanged'), listener); SharedEventEmitter.addListener(getAppEventName(this, 'onUserChanged'), listener);
if (this._authResult) listener(this._user || null); if (this._authResult) listener(this._user || null);
return this._offUserChanged.bind(this, listener); return this._offUserChanged.bind(this, listener);
} }
@ -166,24 +193,24 @@ export default class Auth extends ModuleBase {
* @param listener * @param listener
*/ */
_offUserChanged(listener: Function) { _offUserChanged(listener: Function) {
this.log.info('Removing onUserChanged listener'); getLogger(this).info('Removing onUserChanged listener');
this.removeListener(this._getAppEventName('onUserChanged'), listener); SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener);
} }
/** /**
* Sign the current user out * Sign the current user out
* @return {Promise} * @return {Promise}
*/ */
signOut(): Promise<null> { signOut(): Promise<void> {
return this._interceptUserValue(this._native.signOut()); return this._interceptUndefinedUserValue(getNativeModule(this).signOut());
} }
/** /**
* Sign a user in anonymously * Sign a user in anonymously
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInAnonymously(): Promise<Object> { signInAnonymously(): Promise<User> {
return this._interceptUserValue(this._native.signInAnonymously()); return this._interceptUserValue(getNativeModule(this).signInAnonymously());
} }
/** /**
@ -192,8 +219,8 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password * @param {string} password The user's password
* @return {Promise} A promise indicating the completion * @return {Promise} A promise indicating the completion
*/ */
createUserWithEmailAndPassword(email: string, password: string): Promise<Object> { createUserWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(this._native.createUserWithEmailAndPassword(email, password)); 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 * @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion * @return {Promise} A promise that is resolved upon completion
*/ */
signInWithEmailAndPassword(email: string, password: string): Promise<Object> { signInWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(this._native.signInWithEmailAndPassword(email, password)); 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. * @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInWithCustomToken(customToken: string): Promise<Object> { signInWithCustomToken(customToken: string): Promise<User> {
return this._interceptUserValue(this._native.signInWithCustomToken(customToken)); return this._interceptUserValue(getNativeModule(this).signInWithCustomToken(customToken));
} }
/** /**
* Sign the user in with a third-party authentication provider * Sign the user in with a third-party authentication provider
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInWithCredential(credential: CredentialType): Promise<Object> { signInWithCredential(credential: AuthCredential): Promise<User> {
return this._interceptUserValue( return this._interceptUserValue(
this._native.signInWithCredential( getNativeModule(this).signInWithCredential(
credential.providerId, credential.token, credential.secret, credential.providerId, credential.token, credential.secret,
), ),
); );
@ -231,8 +258,8 @@ export default class Auth extends ModuleBase {
* Asynchronously signs in using a phone number. * Asynchronously signs in using a phone number.
* *
*/ */
signInWithPhoneNumber(phoneNumber: string): Promise<Object> { signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return this._native.signInWithPhoneNumber(phoneNumber).then((result) => { return getNativeModule(this).signInWithPhoneNumber(phoneNumber).then((result) => {
return new ConfirmationResult(this, result.verificationId); return new ConfirmationResult(this, result.verificationId);
}); });
} }
@ -254,8 +281,8 @@ export default class Auth extends ModuleBase {
* Send reset password instructions via email * Send reset password instructions via email
* @param {string} email The email to send password reset instructions * @param {string} email The email to send password reset instructions
*/ */
sendPasswordResetEmail(email: string): Promise<Object> { sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void> {
return this._native.sendPasswordResetEmail(email); return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings);
} }
/** /**
@ -266,8 +293,8 @@ export default class Auth extends ModuleBase {
* @param newPassword * @param newPassword
* @return {Promise.<Null>} * @return {Promise.<Null>}
*/ */
confirmPasswordReset(code: string, newPassword: string): Promise<null> { confirmPasswordReset(code: string, newPassword: string): Promise<void> {
return this._native.confirmPasswordReset(code, newPassword); return getNativeModule(this).confirmPasswordReset(code, newPassword);
} }
/** /**
@ -277,8 +304,8 @@ export default class Auth extends ModuleBase {
* @param code * @param code
* @return {Promise.<Null>} * @return {Promise.<Null>}
*/ */
applyActionCode(code: string): Promise<any> { applyActionCode(code: string): Promise<void> {
return this._native.applyActionCode(code); return getNativeModule(this).applyActionCode(code);
} }
/** /**
@ -288,16 +315,16 @@ export default class Auth extends ModuleBase {
* @param code * @param code
* @return {Promise.<any>|Promise<ActionCodeInfo>} * @return {Promise.<any>|Promise<ActionCodeInfo>}
*/ */
checkActionCode(code: string): Promise<any> { checkActionCode(code: string): Promise<void> {
return this._native.checkActionCode(code); return getNativeModule(this).checkActionCode(code);
} }
/** /**
* Get the currently signed in user * Get the currently signed in user
* @return {Promise} * @return {Promise}
*/ */
getCurrentUser(): Promise<Object> { getCurrentUser(): Promise<User | null> {
return this._interceptUserValue(this._native.getCurrentUser()); return this._interceptUserValue(getNativeModule(this).getCurrentUser());
} }
/** /**
@ -305,7 +332,7 @@ export default class Auth extends ModuleBase {
* @return {Promise} * @return {Promise}
*/ */
fetchProvidersForEmail(email: string): Promise<Array<String>> { 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; return this._user;
} }
get namespace(): string {
return 'firebase:auth';
}
/** /**
* KNOWN UNSUPPORTED METHODS * KNOWN UNSUPPORTED METHODS
*/ */
getRedirectResult() { getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'getRedirectResult')); throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult'));
} }
setPersistence() { setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'setPersistence')); throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence'));
} }
signInAndRetrieveDataWithCredential() { signInAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInAndRetrieveDataWithCredential')); throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInAndRetrieveDataWithCredential'));
} }
signInWithPopup() { signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInWithPopup')); throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup'));
} }
signInWithRedirect() { 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'; const providerId = 'password';
export default class EmailAuthProvider { 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.'); throw new Error('`new EmailAuthProvider()` is not supported on the native Firebase SDKs.');
} }
static get PROVIDER_ID() { static get PROVIDER_ID(): string {
return providerId; return providerId;
} }
static credential(email, password) { static credential(email: string, password: string): AuthCredential {
return { return {
token: email, token: email,
secret: password, secret: password,

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,9 @@
/**
* @flow
* TwitterAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
const providerId = 'twitter.com'; const providerId = 'twitter.com';
export default class TwitterAuthProvider { 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.'); throw new Error('`new TwitterAuthProvider()` is not supported on the native Firebase SDKs.');
} }
static get PROVIDER_ID() { static get PROVIDER_ID(): string {
return providerId; return providerId;
} }
static credential(token, secret) { static credential(token: string, secret: string): AuthCredential {
return { return {
token, token,
secret, 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 * @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 * @class Config
*/ */
export default class RemoteConfig extends ModuleBase { export default class RemoteConfig extends ModuleBase {
static _NAMESPACE = 'config'; _developerModeEnabled: boolean;
static _NATIVE_MODULE = 'RNFirebaseRemoteConfig';
constructor(firebaseApp: Object, options: Object = {}) { constructor(app: App) {
super(firebaseApp, options); super(app, {
this.developerModeEnabled = false; moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;
} }
/** /**
@ -21,12 +40,12 @@ export default class RemoteConfig extends ModuleBase {
* @returns {*} * @returns {*}
* @private * @private
*/ */
_nativeValueToJS(nativeValue) { _nativeValueToJS(nativeValue: NativeValue) {
return { return {
source: nativeValue.source, source: nativeValue.source,
val() { val() {
if (nativeValue.boolValue !== null && (nativeValue.stringValue === 'true' || nativeValue.stringValue === 'false' || nativeValue.stringValue === null)) return nativeValue.boolValue; 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; if (nativeValue.dataValue !== nativeValue.stringValue && (nativeValue.stringValue == null || nativeValue.stringValue === '')) return nativeValue.dataValue;
return nativeValue.stringValue; 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 * Enable Remote Config developer mode to allow for frequent refreshes of the cache
*/ */
enableDeveloperMode() { enableDeveloperMode() {
if (!this.developerModeEnabled) { if (!this._developerModeEnabled) {
this.log.debug('Enabled developer mode'); getLogger(this).debug('Enabled developer mode');
this._native.enableDeveloperMode(); getNativeModule(this).enableDeveloperMode();
this.developerModeEnabled = true; this._developerModeEnabled = true;
} }
} }
@ -51,11 +70,11 @@ export default class RemoteConfig extends ModuleBase {
*/ */
fetch(expiration?: number) { fetch(expiration?: number) {
if (expiration !== undefined) { if (expiration !== undefined) {
this.log.debug(`Fetching remote config data with expiration ${expiration.toString()}`); getLogger(this).debug(`Fetching remote config data with expiration ${expiration.toString()}`);
return this._native.fetchWithExpirationDuration(expiration); return getNativeModule(this).fetchWithExpirationDuration(expiration);
} }
this.log.debug('Fetching remote config data'); getLogger(this).debug('Fetching remote config data');
return this._native.fetch(); 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. * rejects if no Fetched Config was found, or the Fetched Config was already activated.
*/ */
activateFetched() { activateFetched() {
this.log.debug('Activating remote config'); getLogger(this).debug('Activating remote config');
return this._native.activateFetched(); return getNativeModule(this).activateFetched();
} }
/** /**
@ -83,7 +102,7 @@ export default class RemoteConfig extends ModuleBase {
* } * }
*/ */
getValue(key: String) { getValue(key: String) {
return this._native return getNativeModule(this)
.getValue(key || '') .getValue(key || '')
.then(this._nativeValueToJS); .then(this._nativeValueToJS);
} }
@ -103,7 +122,7 @@ export default class RemoteConfig extends ModuleBase {
* } * }
*/ */
getValues(keys: Array<String>) { getValues(keys: Array<String>) {
return this._native return getNativeModule(this)
.getValues(keys || []) .getValues(keys || [])
.then((nativeValues) => { .then((nativeValues) => {
const values: { [String]: Object } = {}; const values: { [String]: Object } = {};
@ -120,7 +139,7 @@ export default class RemoteConfig extends ModuleBase {
* @returns {*|Promise.<Array<String>>} * @returns {*|Promise.<Array<String>>}
*/ */
getKeysByPrefix(prefix?: 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. * @param defaults: A dictionary mapping a String key to a Object values.
*/ */
setDefaults(defaults: Object) { 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 * @param resource: The plist file name or resource ID
*/ */
setDefaultsFromResource(resource: String | number) { 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 { export default class Crash extends ModuleBase {
static _NAMESPACE = 'crash'; constructor(app: App) {
static _NATIVE_MODULE = 'RNFirebaseCrash'; super(app, {
moduleName: MODULE_NAME,
constructor(firebaseApp: Object, options: Object = {}) { multiApp: false,
super(firebaseApp, options); namespace: NAMESPACE,
});
} }
/** /**
@ -14,7 +25,7 @@ export default class Crash extends ModuleBase {
* @param enabled * @param enabled
*/ */
setCrashCollectionEnabled(enabled: boolean): void { 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>} * @returns {Promise.<boolean>}
*/ */
isCrashCollectionEnabled(): 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 * @param {string} message
*/ */
log(message: string): void { 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 * @param {string} tag
*/ */
logcat(level: number, tag: string, message: string): void { 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`; errorMessage = `${errorMessage} - ${stackRows[i]}\r\n`;
} }
this._native.report(errorMessage); getNativeModule(this).report(errorMessage);
} }
} }
export const statics = {};

View File

@ -1,7 +1,11 @@
/* @flow */ /**
* @flow
import { typeOf } from './../../utils'; * Disconnect representation wrapper
import Reference from './reference'; */
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 * @class Disconnect
*/ */
export default class Disconnect { export default class Disconnect {
_database: Database;
ref: Reference; ref: Reference;
path: string; path: string;
@ -28,7 +33,7 @@ export default class Disconnect {
* @returns {*} * @returns {*}
*/ */
set(value: string | Object): Promise<void> { 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 {*} * @returns {*}
*/ */
update(values: Object): Promise<void> { 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 {*} * @returns {*}
*/ */
remove(): Promise<void> { 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 {*} * @returns {*}
*/ */
cancel(): Promise<void> { 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 Reference from './reference';
import TransactionHandler from './transaction'; 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 * @class Database
*/ */
export default class Database extends ModuleBase { export default class Database extends ModuleBase {
static _NAMESPACE = 'database'; _offsetRef: Reference;
static _NATIVE_MODULE = 'RNFirebaseDatabase'; _serverTimeOffset: number;
_transactionHandler: TransactionHandler;
constructor(firebaseApp: Object, options: Object = {}) { constructor(app: App, options: Object = {}) {
super(firebaseApp, options, true); super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._transactionHandler = new TransactionHandler(this); this._transactionHandler = new TransactionHandler(this);
if (this._options.persistence) { if (options.persistence) {
this._native.setPersistence(this._options.persistence); getNativeModule(this).setPersistence(options.persistence);
} }
// server time listener // server time listener
@ -40,22 +57,22 @@ export default class Database extends ModuleBase {
* *
* @return {number} * @return {number}
*/ */
getServerTime() { getServerTime(): number {
return new Date(Date.now() + this._serverTimeOffset); return new Date(Date.now() + this._serverTimeOffset);
} }
/** /**
* *
*/ */
goOnline() { goOnline(): void {
this._native.goOnline(); getNativeModule(this).goOnline();
} }
/** /**
* *
*/ */
goOffline() { goOffline(): void {
this._native.goOffline(); getNativeModule(this).goOffline();
} }
/** /**
@ -63,7 +80,7 @@ export default class Database extends ModuleBase {
* @param path * @param path
* @returns {Reference} * @returns {Reference}
*/ */
ref(path: string) { ref(path: string): Reference {
return new Reference(this, path); return new Reference(this, path);
} }
} }
@ -72,9 +89,9 @@ export const statics = {
ServerValue: NativeModules.RNFirebaseDatabase ? { ServerValue: NativeModules.RNFirebaseDatabase ? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' }, TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
} : {}, } : {},
enableLogging(bool) { enableLogging(enabled: boolean) {
if (NativeModules[Database._NATIVE_MODULE]) { if (NativeModules[MODULE_NAME]) {
NativeModules[Database._NATIVE_MODULE].enableLogging(bool); NativeModules[MODULE_NAME].enableLogging(enabled);
} }
}, },
}; };

View File

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

View File

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

View File

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

View File

@ -2,19 +2,36 @@
* @flow * @flow
* Database Transaction representation wrapper * 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; let transactionId = 0;
/**
* Uses the push id generator to create a transaction id
* @returns {number}
* @private
*/
const generateTransactionId = (): number => {
return transactionId++;
};
/** /**
* @class TransactionHandler * @class TransactionHandler
*/ */
export default class TransactionHandler { export default class TransactionHandler {
constructor(database: Object) { _database: Database;
_transactionListener: Function;
_transactions: { [number]: Object }
constructor(database: Database) {
this._transactions = {}; this._transactions = {};
this._database = database; this._database = database;
this._transactionListener = this._database.addListener( this._transactionListener = SharedEventEmitter.addListener(
this._database._getAppEventName('database_transaction_event'), getAppEventName(this._database, 'database_transaction_event'),
this._handleTransactionEvent.bind(this), this._handleTransactionEvent.bind(this),
); );
} }
@ -27,7 +44,7 @@ export default class TransactionHandler {
* @param applyLocally * @param applyLocally
*/ */
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) { add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) {
const id = this._generateTransactionId(); const id = generateTransactionId();
this._transactions[id] = { this._transactions[id] = {
id, id,
@ -39,22 +56,13 @@ export default class TransactionHandler {
started: true, started: true,
}; };
this._database._native.transactionStart(reference.path, id, applyLocally); getNativeModule(this._database).transactionStart(reference.path, id, applyLocally);
} }
/** /**
* INTERNALS * INTERNALS
*/ */
/**
* Uses the push id generator to create a transaction id
* @returns {string}
* @private
*/
_generateTransactionId(): string {
return transactionId++;
}
/** /**
* *
* @param event * @param event
@ -70,7 +78,7 @@ export default class TransactionHandler {
case 'complete': case 'complete':
return this._handleComplete(event); return this._handleComplete(event);
default: default:
this.log.warn(`Unknown transaction event type: '${event.type}'`, event); getLogger(this._database).warn(`Unknown transaction event type: '${event.type}'`, event);
return undefined; return undefined;
} }
} }
@ -96,7 +104,7 @@ export default class TransactionHandler {
abort = true; 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) { if (transaction && !transaction.completed) {
transaction.completed = true; transaction.completed = true;
try { try {
transaction.onComplete(new Error(event.error.message, event.error.code), null); transaction.onComplete(event.error, false, null);
} finally { } finally {
setImmediate(() => { setImmediate(() => {
delete this._transactions[event.id]; 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 * CollectionReference representation wrapper
*/ */
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import Path from './Path';
import Query from './Query'; import Query from './Query';
import QuerySnapshot from './QuerySnapshot';
import { firestoreAutoId } from '../../utils'; 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 * @class CollectionReference
*/ */
export default class CollectionReference { export default class CollectionReference {
_collectionPath: Path; _collectionPath: Path;
_firestore: Object; _firestore: Firestore;
_query: Query; _query: Query;
constructor(firestore: Object, collectionPath: Path) { constructor(firestore: Firestore, collectionPath: Path) {
this._collectionPath = collectionPath; this._collectionPath = collectionPath;
this._firestore = firestore; this._firestore = firestore;
this._query = new Query(firestore, collectionPath); this._query = new Query(firestore, collectionPath);
} }
get firestore(): Object { get firestore(): Firestore {
return this._firestore; return this._firestore;
} }
@ -55,47 +57,43 @@ export default class CollectionReference {
} }
// From Query // From Query
endAt(fieldValues: any): Query { endAt(...snapshotOrVarArgs: any[]): Query {
return this._query.endAt(fieldValues); return this._query.endAt(snapshotOrVarArgs);
} }
endBefore(fieldValues: any): Query { endBefore(...snapshotOrVarArgs: any[]): Query {
return this._query.endBefore(fieldValues); return this._query.endBefore(snapshotOrVarArgs);
} }
get(): Promise<QuerySnapshot> { get(): Promise<QuerySnapshot> {
return this._query.get(); return this._query.get();
} }
limit(n: number): Query { limit(limit: number): Query {
return this._query.limit(n); return this._query.limit(limit);
} }
offset(n: number): Query { onSnapshot(
return this._query.offset(n); optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
): () => void {
return this._query.onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError);
} }
onSnapshot(onNext: () => any, onError?: () => any): () => void { orderBy(fieldPath: string, directionStr?: FirestoreQueryDirection): Query {
return this._query.onSnapshot(onNext, onError);
}
orderBy(fieldPath: string, directionStr?: Direction): Query {
return this._query.orderBy(fieldPath, directionStr); return this._query.orderBy(fieldPath, directionStr);
} }
startAfter(fieldValues: any): Query { startAfter(...snapshotOrVarArgs: any[]): Query {
return this._query.startAfter(fieldValues); return this._query.startAfter(snapshotOrVarArgs);
} }
startAt(fieldValues: any): Query { startAt(...snapshotOrVarArgs: any[]): Query {
return this._query.startAt(fieldValues); return this._query.startAt(snapshotOrVarArgs);
} }
stream(): Stream<DocumentSnapshot> { where(fieldPath: string, opStr: FirestoreQueryOperator, value: any): Query {
return this._query.stream();
}
where(fieldPath: string, opStr: Operator, value: any): Query {
return this._query.where(fieldPath, opStr, value); return this._query.where(fieldPath, opStr, value);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,15 +2,21 @@
* @flow * @flow
* Firestore representation wrapper * 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 CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import DocumentSnapshot from './DocumentSnapshot';
import FieldValue from './FieldValue'; import FieldValue from './FieldValue';
import GeoPoint from './GeoPoint'; import GeoPoint from './GeoPoint';
import Path from './Path'; import Path from './Path';
import WriteBatch from './WriteBatch'; 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 = { type CollectionSyncEvent = {
appName: string, appName: string,
@ -28,30 +34,40 @@ type DocumentSyncEvent = {
path: string, path: string,
} }
const NATIVE_EVENTS = [
'firestore_collection_sync_event',
'firestore_document_sync_event',
];
export const MODULE_NAME = 'RNFirebaseFirestore';
export const NAMESPACE = 'firestore';
/** /**
* @class Firestore * @class Firestore
*/ */
export default class Firestore extends ModuleBase { export default class Firestore extends ModuleBase {
static _NAMESPACE = 'firestore';
static _NATIVE_MODULE = 'RNFirebaseFirestore';
_referencePath: Path; _referencePath: Path;
constructor(firebaseApp: Object, options: Object = {}) { constructor(app: App) {
super(firebaseApp, options, true); super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._referencePath = new Path([]); this._referencePath = new Path([]);
this.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onCollectionSnapshot // public event name: onCollectionSnapshot
this._getAppEventName('firestore_collection_sync_event'), getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this), this._onCollectionSyncEvent.bind(this),
); );
this.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onDocumentSnapshot // public event name: onDocumentSnapshot
this._getAppEventName('firestore_document_sync_event'), getAppEventName(this, 'firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this), 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'); 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'); throw new Error('firebase.firestore().runTransaction() coming soon');
} }
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void { setLogLevel(): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel')); 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'); throw new Error('firebase.firestore().settings() coming soon');
} }
/**
* INTERNALS
*/
/** /**
* Internal collection sync listener * Internal collection sync listener
* @param event * @param event
@ -115,9 +127,9 @@ export default class Firestore extends ModuleBase {
*/ */
_onCollectionSyncEvent(event: CollectionSyncEvent) { _onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) { if (event.error) {
this.emit(this._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error); SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
} else { } 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) { _onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) { if (event.error) {
this.emit(this._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error); SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
} else { } 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 = { export const statics = {
FieldValue, FieldValue,
GeoPoint, 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 DocumentReference from '../DocumentReference';
import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue'; import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
@ -6,10 +8,8 @@ import GeoPoint from '../GeoPoint';
import Path from '../Path'; import Path from '../Path';
import { typeOf } from '../../../utils'; import { typeOf } from '../../../utils';
type TypeMap = { import type Firestore from '../';
type: 'array' | 'boolean' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string', import type { FirestoreTypeMap } from '../../../types';
value: any,
}
/* /*
* Functions that build up the data needed to represent * Functions that build up the data needed to represent
@ -17,65 +17,86 @@ type TypeMap = {
* for transmission to the native side * for transmission to the native side
*/ */
export const buildNativeMap = (data: Object): Object => { export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } => {
const nativeData = {}; const nativeData = {};
if (data) { if (data) {
Object.keys(data).forEach((key) => { Object.keys(data).forEach((key) => {
nativeData[key] = buildTypeMap(data[key]); const typeMap = buildTypeMap(data[key]);
if (typeMap) {
nativeData[key] = typeMap;
}
}); });
} }
return nativeData; return nativeData;
}; };
export const buildNativeArray = (array: Object[]): any[] => { export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => {
const nativeArray = []; const nativeArray = [];
if (array) { if (array) {
array.forEach((value) => { array.forEach((value) => {
nativeArray.push(buildTypeMap(value)); const typeMap = buildTypeMap(value);
if (typeMap) {
nativeArray.push(typeMap);
}
}); });
} }
return nativeArray; return nativeArray;
}; };
export const buildTypeMap = (value: any): any => { export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
const typeMap = {};
const type = typeOf(value); const type = typeOf(value);
if (value === null || value === undefined) { if (value === null || value === undefined) {
typeMap.type = 'null'; return {
typeMap.value = null; type: 'null',
value: null,
};
} else if (value === DELETE_FIELD_VALUE) { } else if (value === DELETE_FIELD_VALUE) {
typeMap.type = 'fieldvalue'; return {
typeMap.value = 'delete'; type: 'fieldvalue',
value: 'delete',
};
} else if (value === SERVER_TIMESTAMP_FIELD_VALUE) { } else if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
typeMap.type = 'fieldvalue'; return {
typeMap.value = 'timestamp'; type: 'fieldvalue',
value: 'timestamp',
};
} else if (type === 'boolean' || type === 'number' || type === 'string') { } else if (type === 'boolean' || type === 'number' || type === 'string') {
typeMap.type = type; return {
typeMap.value = value; type,
value,
};
} else if (type === 'array') { } else if (type === 'array') {
typeMap.type = type; return {
typeMap.value = buildNativeArray(value); type,
value: buildNativeArray(value),
};
} else if (type === 'object') { } else if (type === 'object') {
if (value instanceof DocumentReference) { if (value instanceof DocumentReference) {
typeMap.type = 'reference'; return {
typeMap.value = value.path; type: 'reference',
value: value.path,
};
} else if (value instanceof GeoPoint) { } else if (value instanceof GeoPoint) {
typeMap.type = 'geopoint'; return {
typeMap.value = { type: 'geopoint',
latitude: value.latitude, value: {
longitude: value.longitude, latitude: value.latitude,
longitude: value.longitude,
},
}; };
} else if (value instanceof Date) { } else if (value instanceof Date) {
typeMap.type = 'date'; return {
typeMap.value = value.getTime(); type: 'date',
} else { value: value.getTime(),
typeMap.type = 'object'; };
typeMap.value = buildNativeMap(value);
} }
} else { return {
console.warn(`Unknown data type received ${type}`); 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 * 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; let data;
if (nativeData) { if (nativeData) {
data = {}; data = {};
@ -94,7 +115,7 @@ export const parseNativeMap = (firestore: Object, nativeData: Object): Object =>
return data; return data;
}; };
const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => { const parseNativeArray = (firestore: Firestore, nativeArray: FirestoreTypeMap[]): any[] => {
const array = []; const array = [];
if (nativeArray) { if (nativeArray) {
nativeArray.forEach((typeMap) => { nativeArray.forEach((typeMap) => {
@ -104,7 +125,7 @@ const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => {
return array; return array;
}; };
const parseTypeMap = (firestore: Object, typeMap: TypeMap): any => { const parseTypeMap = (firestore: Firestore, typeMap: FirestoreTypeMap): any => {
const { type, value } = typeMap; const { type, value } = typeMap;
if (type === 'null') { if (type === 'null') {
return 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 = { const EVENT_TYPE = {
Link: 'dynamic_link_received', Link: 'dynamic_link_received',
}; };
const NATIVE_EVENTS = [
EVENT_TYPE.Link,
];
export const MODULE_NAME = 'RNFirebaseLinks';
export const NAMESPACE = 'links';
function validateParameters(parameters: Object): void { function validateParameters(parameters: Object): void {
const suportedParametersObject = { const suportedParametersObject = {
dynamicLinkDomain: 'string', dynamicLinkDomain: 'string',
@ -56,14 +71,16 @@ function checkForMandatoryParameters(parameters: Object): void {
* @class Links * @class Links
*/ */
export default class Links extends ModuleBase { export default class Links extends ModuleBase {
static _NAMESPACE = 'links'; constructor(app: App) {
static _NATIVE_MODULE = 'RNFirebaseLinks'; super(app, {
events: NATIVE_EVENTS,
constructor(firebaseApp: Object, options: Object = {}) { moduleName: MODULE_NAME,
super(firebaseApp, options, true); multiApp: false,
namespace: NAMESPACE,
});
} }
get EVENT_TYPE() { get EVENT_TYPE(): Object {
return EVENT_TYPE; return EVENT_TYPE;
} }
@ -71,8 +88,8 @@ export default class Links extends ModuleBase {
* Returns the link that triggered application open * Returns the link that triggered application open
* @returns {Promise.<String>} * @returns {Promise.<String>}
*/ */
getInitialLink() { getInitialLink(): Promise<string> {
return this._native.getInitialLink(); return getNativeModule(this).getInitialLink();
} }
/** /**
@ -81,7 +98,7 @@ export default class Links extends ModuleBase {
* @returns {Function} * @returns {Function}
*/ */
onLink(listener: Function): () => any { onLink(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.Link, listener); const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.Link, listener);
return () => rnListener.remove(); return () => rnListener.remove();
} }
@ -90,11 +107,11 @@ export default class Links extends ModuleBase {
* @param parameters * @param parameters
* @returns {Promise.<String>} * @returns {Promise.<String>}
*/ */
createDynamicLink(parameters: Object = {}): Promise<String> { createDynamicLink(parameters: Object = {}): Promise<string> {
try { try {
checkForMandatoryParameters(parameters); checkForMandatoryParameters(parameters);
validateParameters(parameters); validateParameters(parameters);
return this._native.createDynamicLink(parameters); return getNativeModule(this).createDynamicLink(parameters);
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
@ -109,7 +126,7 @@ export default class Links extends ModuleBase {
try { try {
checkForMandatoryParameters(parameters); checkForMandatoryParameters(parameters);
validateParameters(parameters); validateParameters(parameters);
return this._native.createShortDynamicLink(parameters); return getNativeModule(this).createShortDynamicLink(parameters);
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }

View File

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

View File

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

View File

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

View File

@ -1,16 +1,21 @@
/* @flow */ /**
* @flow
* StorageReference representation wrapper
*/
import ReferenceBase from '../../utils/ReferenceBase'; import ReferenceBase from '../../utils/ReferenceBase';
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task'; 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 * @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
*/ */
export default class StorageReference extends ReferenceBase { export default class StorageReference extends ReferenceBase {
_storage: Storage;
constructor(storage: Storage, path: string) { constructor(storage: Storage, path: string) {
super(path, storage); super(path);
this._storage = storage; this._storage = storage;
} }
@ -18,7 +23,7 @@ export default class StorageReference extends ReferenceBase {
return this.path; return this.path;
} }
toString(): String { toString(): string {
return `gs://${this._storage.app.options.storageBucket}${this.path}`; return `gs://${this._storage.app.options.storageBucket}${this.path}`;
} }
@ -27,24 +32,24 @@ export default class StorageReference extends ReferenceBase {
* @param path * @param path
* @returns {StorageReference} * @returns {StorageReference}
*/ */
child(path: string) { child(path: string): StorageReference {
return new StorageReference(this._module, `${this.path}/${path}`); return new StorageReference(this._storage, `${this.path}/${path}`);
} }
/** /**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete * @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
* @returns {Promise.<T>|*} * @returns {Promise.<T>|*}
*/ */
delete(): Promise<*> { delete(): Promise<void> {
return this._module._native.delete(this.path); return getNativeModule(this._storage).delete(this.path);
} }
/** /**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL * @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
* @returns {Promise.<T>|*} * @returns {Promise.<T>|*}
*/ */
getDownloadURL(): Promise<String> { getDownloadURL(): Promise<string> {
return this._module._native.getDownloadURL(this.path); return getNativeModule(this._storage).getDownloadURL(this.path);
} }
/** /**
@ -52,7 +57,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*} * @returns {Promise.<T>|*}
*/ */
getMetadata(): Promise<Object> { 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>|*} * @returns {Promise.<T>|*}
*/ */
updateMetadata(metadata: Object = {}): Promise<Object> { 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} * @return {Promise}
*/ */
downloadFile(filePath: string): Promise<Object> { 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 * Alias to putFile
* @returns {StorageReference.putFile} * @returns {StorageReference.putFile}
*/ */
get put(): Function { get put(): (Object, Object) => Promise<Object> {
return this.putFile; return this.putFile;
} }
@ -89,6 +94,6 @@ export default class StorageReference extends ReferenceBase {
*/ */
putFile(filePath: Object, metadata: Object = {}): Promise<Object> { putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
const _filePath = filePath.replace('file://', ''); 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 { statics as StorageStatics } from './';
import { isFunction } from './../../utils'; import { isFunction } from './../../utils';
import StorageReference from './reference'; import type Storage from './';
import type StorageReference from './reference';
export const UPLOAD_TASK = 'upload'; export const UPLOAD_TASK = 'upload';
export const DOWNLOAD_TASK = 'download'; export const DOWNLOAD_TASK = 'download';
@ -40,15 +44,15 @@ declare type NextOrObserverType = null |
export default class StorageTask { export default class StorageTask {
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK; type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK;
ref: StorageReference; ref: StorageReference;
storage: StorageReference.storage; storage: Storage;
path: StorageReference.path; path: string;
then: () => Promise<*>; then: () => Promise<*>;
catch: () => Promise<*>; catch: () => Promise<*>;
constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) { constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) {
this.type = type; this.type = type;
this.ref = storageRef; this.ref = storageRef;
this.storage = storageRef._module; this.storage = storageRef._storage;
this.path = storageRef.path; this.path = storageRef.path;
// 'proxy' original promise // 'proxy' original promise

View File

@ -1,42 +1,53 @@
// @flow // @flow
import { NativeModules } from 'react-native'; import { NativeModules } from 'react-native';
// import { version as ReactVersion } from 'react'; import INTERNALS from '../../utils/internals';
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion'; import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
import INTERNALS from './../../internals'; import type App from '../core/firebase-app';
import { isIOS } from './../../utils';
import PACKAGE from './../../../package.json';
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
export default class RNFirebaseUtils { type GoogleApiAvailabilityType = {
static _NAMESPACE = 'utils'; status: number,
static _NATIVE_DISABLED = true; isAvailable: boolean,
static _NATIVE_MODULE = 'RNFirebaseUtils'; 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() { checkPlayServicesAvailability() {
if (isIOS) return null; if (isIOS) return;
const code = this.playServicesAvailability.code; const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) { if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) { if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
this.promptForPlayServices(); this.promptForPlayServices();
} else { } else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(code); const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) { 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 throw new Error(error);
} else { } else {
console.warn(error); console.warn(error);
} }
} }
} }
return null;
} }
promptForPlayServices() { promptForPlayServices() {
@ -54,41 +65,13 @@ export default class RNFirebaseUtils {
return FirebaseCoreModule.makePlayServicesAvailable(); return FirebaseCoreModule.makePlayServicesAvailable();
} }
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
/** /**
* Set the global logging level for all logs. * Set the global logging level for all logs.
* *
* @param booleanOrDebugString * @param logLevel
*/ */
set logLevel(booleanOrDebugString) { set logLevel(logLevel: string) {
INTERNALS.OPTIONS.logLevel = booleanOrDebugString; INTERNALS.OPTIONS.logLevel = logLevel;
}
/**
* 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);
} }
/** /**
@ -105,7 +88,7 @@ export default class RNFirebaseUtils {
* @android * @android
* @param bool * @param bool
*/ */
set errorOnMissingPlayServices(bool: Boolean) { set errorOnMissingPlayServices(bool: boolean) {
INTERNALS.OPTIONS.errorOnMissingPlayServices = bool; INTERNALS.OPTIONS.errorOnMissingPlayServices = bool;
} }
@ -114,18 +97,10 @@ export default class RNFirebaseUtils {
* @android * @android
* @param bool * @param bool
*/ */
set promptOnMissingPlayServices(bool: Boolean) { set promptOnMissingPlayServices(bool: boolean) {
INTERNALS.OPTIONS.promptOnMissingPlayServices = bool; INTERNALS.OPTIONS.promptOnMissingPlayServices = bool;
} }
} }
export const statics = { 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,
// },
};

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 * @flow
*/ */
import { NativeModules } from 'react-native'; import { initialiseLogger } from './log';
import { initialiseNativeModule } from './native';
import Log from '../utils/log'; import type App from '../modules/core/firebase-app';
import INTERNALS from './../internals'; import type { FirebaseModuleConfig, FirebaseNamespace } from '../types';
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,
},
};
export default class ModuleBase { export default class ModuleBase {
_native: Object; _app: App;
_module: string; namespace: FirebaseNamespace;
_options: Object;
_appName: string;
_namespace: string;
_firebaseApp: Object;
_eventEmitter: Object;
static _NAMESPACE: string;
static _NATIVE_MODULE: string;
/** /**
* *
* @param firebaseApp * @param app
* @param options * @param config
* @param withEventEmitter
*/ */
constructor(firebaseApp: Object, options: Object, withEventEmitter: boolean = false) { constructor(app: App, config: FirebaseModuleConfig) {
this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', ''); if (!config.moduleName) {
this._firebaseApp = firebaseApp; throw new Error('Missing module name');
this._appName = firebaseApp._name; }
this._namespace = `${this._appName}:${this._module}`; if (!config.namespace) {
this._options = Object.assign({}, DEFAULTS[this._module] || {}, options); throw new Error('Missing namespace');
}
const { moduleName } = config;
this._app = app;
this.namespace = config.namespace;
// check if native module exists as all native // check if native module exists as all native
// modules are now optionally part of build initialiseNativeModule(this, config);
const nativeModule = NativeModules[this.constructor._NATIVE_MODULE]; initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
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);
}
} }
/** /**
* * Returns the App instance for current module
* @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
* @return {*} * @return {*}
*/ */
get app(): FirebaseApp { get app(): App {
return this._firebaseApp; return this._app;
}
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);
} }
} }

View File

@ -2,8 +2,9 @@
* @flow * @flow
*/ */
export default class ReferenceBase { export default class ReferenceBase {
constructor(path: string, module) { path: string;
this._module = module;
constructor(path: string) {
this.path = path || '/'; this.path = path || '/';
} }
@ -16,8 +17,4 @@ export default class ReferenceBase {
get key(): string | null { get key(): string | null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1); 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 { SharedEventEmitter } from './events';
import DatabaseSnapshot from './../modules/database/snapshot'; import DatabaseSnapshot from '../modules/database/snapshot';
import DatabaseReference from './../modules/database/reference'; import DatabaseReference from '../modules/database/reference';
import { isString, nativeToJSError } from './../utils'; import { isString, nativeToJSError } from '../utils';
type Listener = (DatabaseSnapshot) => any;
type Registration = { type Registration = {
key: String, key: string,
path: String, path: string,
once?: Boolean, once?: boolean,
appName: String, appName: string,
eventType: String, eventType: string,
listener: Function, listener: Listener,
eventRegistrationKey: String, eventRegistrationKey: string,
ref: DatabaseReference, ref: DatabaseReference,
} }
@ -20,16 +25,21 @@ type Registration = {
* Internally used to manage firebase database realtime event * Internally used to manage firebase database realtime event
* subscriptions and keep the listeners in sync in js vs native. * subscriptions and keep the listeners in sync in js vs native.
*/ */
export default class SyncTree { class SyncTree {
constructor(databaseNative) { _nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: { [string]: Listener }}};
constructor() {
this._tree = {}; this._tree = {};
this._reverseLookup = {}; this._reverseLookup = {};
this._databaseNative = databaseNative; if (NativeModules.RNFirebaseDatabase) {
this._nativeEmitter = new NativeEventEmitter(databaseNative); this._nativeEmitter = new NativeEventEmitter(NativeModules.RNFirebaseDatabase);
this._nativeEmitter.addListener( this._nativeEmitter.addListener(
'database_sync_event', 'database_sync_event',
this._handleSyncEvent.bind(this), this._handleSyncEvent.bind(this),
); );
}
} }
/** /**
@ -63,13 +73,13 @@ export default class SyncTree {
// notify native that the registration // notify native that the registration
// no longer exists so it can remove // no longer exists so it can remove
// the native listeners // the native listeners
return this._databaseNative.off(key, eventRegistrationKey); return NativeModules.RNFirebaseDatabase.off(key, eventRegistrationKey);
} }
const { snapshot, previousChildName } = event.data; const { snapshot, previousChildName } = event.data;
// forward on to users .on(successCallback <-- listener // forward on to users .on(successCallback <-- listener
return INTERNALS.SharedEventEmitter.emit( return SharedEventEmitter.emit(
eventRegistrationKey, eventRegistrationKey,
new DatabaseSnapshot(registration.ref, snapshot), new DatabaseSnapshot(registration.ref, snapshot),
previousChildName, previousChildName,
@ -96,7 +106,7 @@ export default class SyncTree {
const error = nativeToJSError(code, message, { ref: registration.ref }); const error = nativeToJSError(code, message, { ref: registration.ref });
// forward on to users .on(successCallback, cancellationCallback <-- listener // 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 // remove the paired event registration - if we received a cancellation
// event then it's guaranteed that they'll be no further value events // event then it's guaranteed that they'll be no further value events
@ -110,7 +120,7 @@ export default class SyncTree {
* @param registration * @param registration
* @return {null} * @return {null}
*/ */
getRegistration(registration): Registration | null { getRegistration(registration: string): Registration | null {
return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null; return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null;
} }
@ -120,17 +130,17 @@ export default class SyncTree {
* @param registrations * @param registrations
* @return {number} * @return {number}
*/ */
removeListenersForRegistrations(registrations) { removeListenersForRegistrations(registrations: string | string[]): number {
if (isString(registrations)) { if (isString(registrations)) {
this.removeRegistration(registrations); this.removeRegistration(registrations);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations); SharedEventEmitter.removeAllListeners(registrations);
return 1; return 1;
} }
if (!Array.isArray(registrations)) return 0; if (!Array.isArray(registrations)) return 0;
for (let i = 0, len = registrations.length; i < len; i++) { for (let i = 0, len = registrations.length; i < len; i++) {
this.removeRegistration(registrations[i]); this.removeRegistration(registrations[i]);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations[i]); SharedEventEmitter.removeAllListeners(registrations[i]);
} }
return registrations.length; return registrations.length;
@ -143,13 +153,13 @@ export default class SyncTree {
* @param registrations * @param registrations
* @return {Array} array of registrations removed * @return {Array} array of registrations removed
*/ */
removeListenerRegistrations(listener, registrations) { removeListenerRegistrations(listener: () => any, registrations: string[]) {
if (!Array.isArray(registrations)) return []; if (!Array.isArray(registrations)) return [];
const removed = []; const removed = [];
for (let i = 0, len = registrations.length; i < len; i++) { for (let i = 0, len = registrations.length; i < len; i++) {
const registration = registrations[i]; const registration = registrations[i];
const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(registration); const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
if (subscriptions) { if (subscriptions) {
for (let j = 0, l = subscriptions.length; j < l; j++) { for (let j = 0, l = subscriptions.length; j < l; j++) {
const subscription = subscriptions[j]; const subscription = subscriptions[j];
@ -173,7 +183,7 @@ export default class SyncTree {
* @param path * @param path
* @return {Array} * @return {Array}
*/ */
getRegistrationsByPath(path): Array { getRegistrationsByPath(path: string): string[] {
const out = []; const out = [];
const eventKeys = Object.keys(this._tree[path] || {}); const eventKeys = Object.keys(this._tree[path] || {});
@ -191,7 +201,7 @@ export default class SyncTree {
* @param eventType * @param eventType
* @return {Array} * @return {Array}
*/ */
getRegistrationsByPathEvent(path, eventType): Array { getRegistrationsByPathEvent(path: string, eventType: string): string[] {
if (!this._tree[path]) return []; if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return []; if (!this._tree[path][eventType]) return [];
@ -206,9 +216,9 @@ export default class SyncTree {
* @param listener * @param listener
* @return {Array} * @return {Array}
*/ */
getOneByPathEventListener(path: string, eventType: string, listener: Function): Array { getOneByPathEventListener(path: string, eventType: string, listener: Function): ?string {
if (!this._tree[path]) return []; if (!this._tree[path]) return null;
if (!this._tree[path][eventType]) return []; if (!this._tree[path][eventType]) return null;
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]); const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
@ -228,22 +238,28 @@ export default class SyncTree {
* @param listener * @param listener
* @return {String} * @return {String}
*/ */
addRegistration(parameters: Registration, listener): String { addRegistration(registration: Registration): string {
const { path, eventType, eventRegistrationKey, once } = parameters; const {
eventRegistrationKey,
eventType,
listener,
once,
path,
} = registration;
if (!this._tree[path]) this._tree[path] = {}; if (!this._tree[path]) this._tree[path] = {};
if (!this._tree[path][eventType]) this._tree[path][eventType] = {}; if (!this._tree[path][eventType]) this._tree[path][eventType] = {};
this._tree[path][eventType][eventRegistrationKey] = listener; this._tree[path][eventType][eventRegistrationKey] = listener;
this._reverseLookup[eventRegistrationKey] = Object.assign({ listener }, parameters); this._reverseLookup[eventRegistrationKey] = registration;
if (once) { if (once) {
INTERNALS.SharedEventEmitter.once( SharedEventEmitter.once(
eventRegistrationKey, eventRegistrationKey,
this._onOnceRemoveRegistration(eventRegistrationKey, listener), this._onOnceRemoveRegistration(eventRegistrationKey, listener),
); );
} else { } else {
INTERNALS.SharedEventEmitter.addListener(eventRegistrationKey, listener); SharedEventEmitter.addListener(eventRegistrationKey, listener);
} }
return eventRegistrationKey; return eventRegistrationKey;
@ -256,7 +272,7 @@ export default class SyncTree {
* @param registration * @param registration
* @return {boolean} * @return {boolean}
*/ */
removeRegistration(registration: String): Boolean { removeRegistration(registration: string): boolean {
if (!this._reverseLookup[registration]) return false; if (!this._reverseLookup[registration]) return false;
const { path, eventType, once } = this._reverseLookup[registration]; 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 // automatically unsubscribed on native when the first event is sent
const registrationObj = this._reverseLookup[registration]; const registrationObj = this._reverseLookup[registration];
if (registrationObj && !once) { if (registrationObj && !once) {
this._databaseNative.off(registrationObj.key, registration); NativeModules.RNFirebaseDatabase.off(registrationObj.key, registration);
} }
delete this._tree[path][eventType][registration]; delete this._tree[path][eventType][registration];
@ -292,10 +308,11 @@ export default class SyncTree {
* @private * @private
*/ */
_onOnceRemoveRegistration(registration, listener) { _onOnceRemoveRegistration(registration, listener) {
return (...args) => { return (...args: any[]) => {
this.removeRegistration(registration); this.removeRegistration(registration);
listener(...args); 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 // modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const hasOwnProperty = Object.hasOwnProperty; const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50; // const DEFAULT_CHUNK_SIZE = 50;
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
const REGEXP_FIELD_NAME = new RegExp(
`^(?:\\.?((?:(?:[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. * Deep get a value from an object.
* @website https://github.com/Salakar/deeps * @website https://github.com/Salakar/deeps
@ -26,9 +18,7 @@ const REGEXP_FIELD_PATH = new RegExp(
* @param joiner * @param joiner
* @returns {*} * @returns {*}
*/ */
export function deepGet(object: Object, export function deepGet(object: Object, path: string, joiner?: string = '/'): any {
path: string,
joiner?: string = '/'): any {
const keys = path.split(joiner); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -52,9 +42,7 @@ export function deepGet(object: Object,
* @param joiner * @param joiner
* @returns {*} * @returns {*}
*/ */
export function deepExists(object: Object, export function deepExists(object: Object, path: string, joiner?: string = '/'): boolean {
path: string,
joiner?: string = '/'): boolean {
const keys = path.split(joiner); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -98,7 +86,7 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2 * @param arr2
* @returns {boolean} * @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)) { if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false; return false;
} }
@ -112,8 +100,8 @@ export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean {
* @param item * @param item
* @returns {boolean} * @returns {boolean}
*/ */
export function isObject(item: any): boolean { export function isObject(item: mixed): boolean %checks {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null); return item ? (typeof item === 'object' && !Array.isArray(item) && item !== null) : false;
} }
/** /**
@ -121,8 +109,8 @@ export function isObject(item: any): boolean {
* @param item * @param item
* @returns {*|boolean} * @returns {*|boolean}
*/ */
export function isFunction(item?: any): boolean { export function isFunction(item?: mixed): boolean %checks {
return Boolean(item && typeof item === 'function'); return item ? typeof item === 'function' : false;
} }
/** /**
@ -130,20 +118,10 @@ export function isFunction(item?: any): boolean {
* @param value * @param value
* @return {boolean} * @return {boolean}
*/ */
export function isString(value: any): boolean { export function isString(value: mixed): boolean %checks {
return typeof value === 'string'; 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 // platform checks
export const isIOS = Platform.OS === 'ios'; export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android'; export const isAndroid = Platform.OS === 'android';
@ -167,7 +145,7 @@ export function tryJSONParse(string: string | null): any {
* @param data * @param data
* @returns {*} * @returns {*}
*/ */
export function tryJSONStringify(data: any): string | null { export function tryJSONStringify(data: mixed): string | null {
try { try {
return JSON.stringify(data); return JSON.stringify(data);
} catch (jsonError) { } catch (jsonError) {
@ -362,25 +340,6 @@ export function nativeToJSError(code: string, message: string, additionalProps?:
return error; 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 * @param object
@ -412,21 +371,21 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback * @param optionalCallback
* @return {Promise} * @return {Promise}
*/ */
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function) { export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> {
if (!isFunction(optionalCallback)) return promise; if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => { return promise.then((result) => {
// some of firebase internal tests & methods only check/return one arg // 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 // 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); optionalCallback(null);
} else { } else if (optionalCallback) {
optionalCallback(null, result); optionalCallback(null, result);
} }
return Promise.resolve(result); return Promise.resolve(result);
}).catch((error) => { }).catch((error) => {
optionalCallback(error); if (optionalCallback) optionalCallback(error);
return Promise.reject(error); return Promise.reject(error);
}); });
} }

View File

@ -1,9 +1,7 @@
import { Platform, NativeModules } from 'react-native'; /**
* @flow
import EventEmitter from './utils/emitter/EventEmitter'; */
import SyncTree from './utils/SyncTree'; import { Platform } from 'react-native';
const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
const NAMESPACE_PODS = { const NAMESPACE_PODS = {
admob: 'Firebase/AdMob', admob: 'Firebase/AdMob',
@ -23,26 +21,32 @@ const GRADLE_DEPS = {
}; };
const PLAY_SERVICES_CODES = { const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
1: { 1: {
code: 'SERVICE_MISSING', code: 'SERVICE_MISSING',
message: 'Google Play services is missing on this device.', message: 'Google Play services is missing on this device.',
}, },
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: { 2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED', code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.', 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: { 3: {
code: 'SERVICE_DISABLED', code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.', 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: { 9: {
code: 'SERVICE_INVALID', code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.', 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: { 18: {
code: 'SERVICE_UPDATING', code: 'SERVICE_UPDATING',
message: 'Google Play services is currently being updated on this device.', 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: { 19: {
code: 'SERVICE_MISSING_PERMISSION', code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.', message: 'Google Play service doesn\'t have one or more required permissions.',
@ -61,11 +65,6 @@ export default {
checkedPlayServices: false, checkedPlayServices: false,
}, },
// track all initialized firebase apps
APPS: {
[DEFAULT_APP_NAME]: null,
},
STRINGS: { STRINGS: {
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' + 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.', 'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.',
@ -92,35 +91,35 @@ export default {
/** /**
* @return {string} * @return {string}
*/ */
ERROR_MISSING_CB(method) { ERROR_MISSING_CB(method: string) {
return `Missing required callback for method ${method}().`; return `Missing required callback for method ${method}().`;
}, },
/** /**
* @return {string} * @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 `Missing required argument of type '${type}' for method '${method}()'.`;
}, },
/** /**
* @return {string} * @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 `Missing required argument '${name}' of type '${type}' for method '${method}()'.`;
}, },
/** /**
* @return {string} * @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 `Invalid value for argument '${name}' expected value '${expected}' but got '${got}'.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_PROTECTED_PROP(name) { ERROR_PROTECTED_PROP(name: string) {
return `Property '${name}' is protected and can not be overridden by extendApp.`; return `Property '${name}' is protected and can not be overridden by extendApp.`;
}, },
@ -129,7 +128,7 @@ export default {
* @param namespace * @param namespace
* @param nativeModule * @param nativeModule
*/ */
ERROR_MISSING_MODULE(namespace, nativeModule) { ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
const snippet = `firebase.${namespace}()`; const snippet = `firebase.${namespace}()`;
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` + 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} * @return {string}
*/ */
ERROR_APP_NOT_INIT(appName) { ERROR_APP_NOT_INIT(appName: string) {
return `The [${appName}] firebase app has not been initialized!`; return `The [${appName}] firebase app has not been initialized!`;
}, },
@ -160,43 +159,43 @@ export default {
* @return {string} * @return {string}
* @constructor * @constructor
*/ */
ERROR_MISSING_OPT(optName) { ERROR_MISSING_OPT(optName: string) {
return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`; return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_NOT_APP(namespace) { ERROR_NOT_APP(namespace: string) {
return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`; return `Invalid App instance passed to firebase.${namespace}(app <--).`;
}, },
/** /**
* @return {string} * @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 `${className}.${method}() is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @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 `${className}.${property} is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_UNSUPPORTED_MODULE_METHOD(module, method) { ERROR_UNSUPPORTED_MODULE_METHOD(namespace: string, method: string) {
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`; return `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_PLAY_SERVICES(statusCode) { ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode]; 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.'; 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` + return `${start}\r\n\r\n` +
'-------------------------\r\n' + '-------------------------\r\n' +
(knownError ? (knownError ?
`${knownError.code}: ${knownError.message} (code ${statusCode})` : `${knownError.code}: ${knownError.message} (code ${statusCode})` :
`A specific play store availability reason reason was not available (unknown code: ${statusCode || null})` `A specific play store availability reason reason was not available (unknown code: ${statusCode})`
) + ) +
'\r\n-------------------------' + '\r\n-------------------------' +
'\r\n\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:' + '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'; '\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 { windowOrGlobal } from './';
import type ModuleBase from './ModuleBase';
((base) => { ((base) => {
window = base || window; window = base || window;
// $FlowFixMe: Why are we using localStorage at all?
if (!window.localStorage) window.localStorage = {}; if (!window.localStorage) window.localStorage = {};
})(windowOrGlobal); })(windowOrGlobal);
// clean up time // 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 { export default class Log {
static createLogger(namespace) { static createLogger(namespace) {
return require('bows')(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