Merge branch 'master' of https://github.com/invertase/react-native-firebase into firestore-transactions

This commit is contained in:
Salakar 2018-02-11 16:17:38 +00:00
commit 8e00b7e607
172 changed files with 9602 additions and 3921 deletions

View File

@ -1,46 +1,36 @@
{
"extends": "airbnb",
"extends": [
"airbnb",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"flowtype"
"flowtype",
"prettier"
],
"env": {
"es6": true,
"jasmine": true
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": {
"prettier/prettier": ["error", {
"trailingComma": "es5",
"singleQuote": true
}],
"react/forbid-prop-types": "warn",
"react/jsx-filename-extension": [
"off", { "extensions": [".js", ".jsx"] }
],
"class-methods-use-this": 0,
"no-plusplus": 0,
"no-underscore-dangle": 0,
"no-return-assign": 0,
"no-undef": 0,
"no-use-before-define": 0,
"arrow-body-style": 0,
"import/prefer-default-export": 0,
"radix": 0,
"new-cap": 0,
"max-len": 0,
"no-continue": 0,
"no-console": 0,
"global-require": 0,
"import/extensions": 0,
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0,
"no-unused-expressions": 0,
"flowtype/no-unused-expressions": ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}]
"no-plusplus": 0,
"no-undef": 0,
"no-underscore-dangle": "off",
"no-use-before-define": 0
},
"globals": {
"__DEV__": true,

View File

@ -3,7 +3,7 @@
.*/*[.]android.js
; Ignore "BUCK" generated dirs
.*/node_modules/react-native/\.buckd/
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
@ -16,22 +16,8 @@
; Ignore polyfills
.*/Libraries/polyfills/.*
# React Native problems
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaCalculator.js.flow
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaPatcher.js.flow
.*/node_modules/metro-bundler/src/node-haste/AssetResolutionCache.js.flow
.*/node_modules/metro-bundler/src/node-haste/DependencyGraph.js.flow
#.*/node_modules/react-native/Libraries/Animated/src/nodes/AnimatedStyle.js
#.*/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableFlatList.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableListView.js
#.*/node_modules/react-native/Libraries/Image/ImageBackground.js
#.*/node_modules/react-native/Libraries/Lists/FlatList.js
#.*/node_modules/react-native/Libraries/Lists/MetroListView.js
#.*/node_modules/react-native/Libraries/Lists/SectionList.js
#.*/node_modules/react-native/Libraries/Lists/ViewabilityHelper.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedList.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedSectionList.js
; Ignore metro
.*/node_modules/metro/.*
# Ignore dist folder
.*/dist/.*
@ -44,12 +30,13 @@
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
module.system=haste
emoji=true
module.system=haste
munge_underscores=true
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'
@ -63,10 +50,9 @@ suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
@ -74,4 +60,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
unsafe.enable_getters_and_setters=true
[version]
^0.56.0
^0.61.0

View File

@ -5,7 +5,7 @@ The issue list of this repo is exclusively for bug reports.
1) For feature requests, please use our Canny board: https://react-native-firebase.canny.io/feature-requests
2) For questions and support please use our Discord chat: https://discord.gg/t6bdqMs or Stack Overflow: https://stackoverflow.com/questions/tagged/react-native-firebase
2) For questions and support please use our Discord chat: https://discord.gg/C9aK28N 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.
-->
@ -14,27 +14,31 @@ The issue list of this repo is exclusively for bug reports.
<!--- Please write your issue here, provide as much detail as you can, code snippets, key files which will help us to debug such as your `Podfile` and/or `app/build.gradle` file). -->
### Environment
<!--- (e.g. iOS, Android, Both) --->
1. Application Target Platform:
1. Application Target Platform:
<!--- (e.g. macOS Sierra, Windows 10) --->
2. Development Operating System:
2. Development Operating System:
<!--- (Xcode or Android Studio version, iOS or Android SDK version - if relevant) --->
3. Build Tools:
3. Build Tools:
<!--- (e.g. 0.45.1) --->
4. React Native version:
4. React Native version:
<!--- (e.g. 2.1.3) --->
5. RNFirebase Version:
5. RNFirebase Version:
<!--- (e.g. database, auth, messaging, analytics etc - or N/A if not applicable) --->
6. Firebase Module:
6. Firebase Module:
<!-- Love react-native-firebase? Please consider supporting our collective:
👉 https://opencollective.com/react-native-firebase/donate -->

2
.gitignore vendored
View File

@ -10,6 +10,7 @@ npm-debug.log
*.perspectivev3
*.xcuserstate
project.xcworkspace/
atlassian-ide-plugin
xcuserdata/
# Example
@ -78,3 +79,4 @@ local.properties
**/ios/Pods/**
**/ios/ReactNativeFirebaseDemo.xcworkspace/
dist
version.js

46
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss@invertase.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -4,16 +4,17 @@
First, thank you for considering contributing to react-native-firebase! It's people like you that make the open source community such a great community! 😊
We welcome any type of contribution, not only code. You can help with
- **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- **Marketing**: writing blog posts, howto's, printing stickers, ...
- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-firebase).
We welcome any type of contribution, not only code. You can help with
* **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
* **Marketing**: writing blog posts, howto's, printing stickers, ...
* **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
* **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
* **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-firebase).
## Your First Contribution
Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
Working on your first Pull Request? You can learn how from this _free_ series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
## Submitting code
@ -41,14 +42,12 @@ You can also reach us at oss@invertase.io
Thank you to all the people who have already contributed to react-native-firebase!
<a href="graphs/contributors"><img src="https://opencollective.com/react-native-firebase/contributors.svg?width=890" /></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/react-native-firebase#backer)]
<a href="https://opencollective.com/react-native-firebase#backers" target="_blank"><img src="https://opencollective.com/react-native-firebase/backers.svg?width=890"></a>
### Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/react-native-firebase#sponsor))

View File

@ -11,7 +11,7 @@
<a href="/LICENSE"><img src="https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square" alt="License"></a>
<a href="#backers"><img src="https://opencollective.com/react-native-firebase/backers/badge.svg" alt="Backers on Open Collective"></a>
<a href="#sponsors"><img src="https://opencollective.com/react-native-firebase/sponsors/badge.svg" alt="Sponsors on Open Collective"></a>
<a href="https://discord.gg/t6bdqMs"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square" alt="Chat"></a>
<a href="https://discord.gg/C9aK28N"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square" alt="Chat"></a>
<a href="https://twitter.com/rnfirebase"><img src="https://img.shields.io/twitter/follow/rnfirebase.svg?style=social&label=Follow" alt="Follow on Twitter"></a>
</p>
@ -30,43 +30,44 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
---
## Supported Firebase Features
> The Web SDK column indicates what modules/functionality from the Web SDK are usable within React Native.
> The Web SDK column indicates what modules/functionality from the Web SDK are usable within React Native.
> '**?**' indicates partial support
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK |
| ---------------------- | :---: | :---: | :---: | :---: | :---: | :---: |
| **AdMob** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Analytics**             | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ |
| **Core** | ❌ |**?**| ✅ | ✅ | ✅ | ✅ |
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ | ✅ |**?**|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| **Crashlytics**           | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ |**?**|**?**| ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Remote Config** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Storage** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**|
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK |
| -------------------------- | :----: | :----: | :----: | :----: | :----: | :-----: |
| **AdMob** | | | | | | |
| **Analytics**             | | | | | | |
| **App Indexing**           | | | | | | |
| **Authentication** | | | | | | |
| _-- Phone Auth_ | | | | | | |
| **Core** | | **?** | | | | |
| _-- Multiple Apps_ | | | | | | |
| **Cloud Firestore** | | | | | | **?** |
| **Cloud Messaging (FCM)** | | | | | | **?** |
| **Crashlytics**           | | | | | | |
| **Crash Reporting** | | | | | | |
| **Dynamic Links** | | | | | | |
| **Invites** | | | | **?** | **?** | |
| **Performance Monitoring** | | | | | | |
| **Realtime Database** | | | | | | |
| _-- Offline Persistence_ | | | | | | **?** |
| _-- Transactions_ | | | | | | |
| **Remote Config** | | | | | | |
| **Storage** | | | | | | **?** |
---
### Supported versions - React Native / Firebase
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X | 3.2.X |
|------------------------|-------------|-------------|-----------------|----------|-------------|----------|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + | 0.48 - 0.49 | 0.50 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + | 11.6.0 + | 11.6.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + | 4.5.0 + | 4.7.0 + |
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X | 3.2.X |
| -------------------- | ----------- | ----------- | ------------- | -------- | ----------- | -------- |
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + | 0.48 - 0.49 | 0.50 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + | 11.6.0 + | 11.6.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + | 4.5.0 + | 4.7.0 + |
---
@ -76,7 +77,7 @@ To check out our latest docs, visit [rnfirebase.io](https://rnfirebase.io)
## Questions
For questions and support please use our [Discord chat](https://discord.gg/t6bdqMs) or [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native-firebase). The issue list of this repo is **exclusively** for bug reports.
For questions and support please use our [Discord chat](https://discord.gg/C9aK28N) or [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native-firebase). The issue list of this repo is **exclusively** for bug reports.
## Issues
@ -96,7 +97,7 @@ Detailed changes for each release are documented in the [releases notes](https:/
RNFirebase is an Apache-2.0 licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [sponsors](#sponsors) and [backers](#backers). If you'd like to join them, please consider:
- [Become a backer or sponsor on Open Collective](https://opencollective.com/react-native-firebase).
* [Become a backer or sponsor on Open Collective](https://opencollective.com/react-native-firebase).
### Sponsors
@ -131,4 +132,4 @@ Thank you to all the people who have already contributed to RNFirebase!
## License
- See [LICENSE](/LICENSE)
* See [LICENSE](/LICENSE)

View File

@ -1,5 +1,5 @@
buildscript {
ext.firebaseVersion = '11.4.2'
ext.firebaseVersion = '11.8.0'
repositories {
jcenter()
maven {

View File

@ -6,6 +6,7 @@ import android.util.Log;
import android.net.Uri;
import android.support.annotation.NonNull;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.List;
@ -35,7 +36,9 @@ import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseAuthProvider;
import com.google.firebase.auth.FirebaseUserMetadata;
import com.google.firebase.auth.GithubAuthProvider;
import com.google.firebase.auth.OAuthProvider;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.auth.ProviderQueryResult;
@ -91,13 +94,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
FirebaseUser user = firebaseAuth.getCurrentUser();
WritableMap msgMap = Arguments.createMap();
if (user != null) {
msgMap.putBoolean("authenticated", true);
msgMap.putString("appName", appName); // for js side distribution
msgMap.putMap("user", firebaseUserToMap(user));
Utils.sendEvent(mReactContext, "auth_state_changed", msgMap);
} else {
msgMap.putString("appName", appName); // for js side distribution
msgMap.putBoolean("authenticated", false);
Utils.sendEvent(mReactContext, "auth_state_changed", msgMap);
}
}
@ -178,11 +179,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* signOut
*
* @param promise
*/
@ReactMethod
public void signOut(String appName, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -197,13 +193,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* signInAnonymously
*
* @param promise
*/
@ReactMethod
public void signInAnonymously(String appName, final Promise promise) {
signInAnonymously(appName, promise, false);
}
@ReactMethod
public void signInAnonymouslyAndRetrieveData(String appName, final Promise promise) {
signInAnonymously(appName, promise, true);
}
public void signInAnonymously(String appName, final Promise promise, final boolean withData) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -213,7 +213,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInAnonymously:onComplete:success");
promiseWithUser(authResult.getUser(), promise);
if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@ -234,6 +238,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void createUserWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) {
createUserWithEmailAndPassword(appName, email, password, promise, false);
}
@ReactMethod
public void createUserAndRetrieveDataWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) {
createUserWithEmailAndPassword(appName, email, password, promise, true);
}
public void createUserWithEmailAndPassword(String appName, final String email, final String password, final Promise promise, final boolean withData) {
Log.d(TAG, "createUserWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -243,7 +256,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "createUserWithEmailAndPassword:onComplete:success");
promiseWithUser(authResult.getUser(), promise);
if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@ -264,6 +281,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void signInWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) {
signInWithEmailAndPassword(appName, email, password, promise, false);
}
@ReactMethod
public void signInAndRetrieveDataWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) {
signInWithEmailAndPassword(appName, email, password, promise, true);
}
public void signInWithEmailAndPassword(String appName, final String email, final String password, final Promise promise, final boolean withData) {
Log.d(TAG, "signInWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -273,7 +299,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithEmailAndPassword:onComplete:success");
promiseWithUser(authResult.getUser(), promise);
if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@ -286,14 +316,18 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
/**
* signInWithCustomToken
*
* @param token
* @param promise
*/
@ReactMethod
public void signInWithCustomToken(String appName, final String token, final Promise promise) {
signInWithCustomToken(appName, token, promise, false);
}
@ReactMethod
public void signInAndRetrieveDataWithCustomToken(String appName, final String token, final Promise promise) {
signInWithCustomToken(appName, token, promise, true);
}
public void signInWithCustomToken(String appName, final String token, final Promise promise, final boolean withData) {
Log.d(TAG, "signInWithCustomToken");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -303,7 +337,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithCustomToken:onComplete:success");
promiseWithUser(authResult.getUser(), promise);
if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@ -350,26 +388,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* getCurrentUser - returns the current user, probably no need for this due to
* js side already listening for user auth changes
*
* @param promise
*/
@ReactMethod
public void getCurrentUser(String appName, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
FirebaseUser user = firebaseAuth.getCurrentUser();
Log.d(TAG, "getCurrentUser");
if (user == null) {
promiseNoUser(promise, false);
} else {
promiseWithUser(user, promise);
}
}
/* ----------------------
* .currentUser methods
* ---------------------- */
@ -601,16 +619,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* signInWithCredential
*
* @param provider
* @param authToken
* @param authSecret
* @param promise
*/
@ReactMethod
public void signInWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) {
signInWithCredential(appName, provider, authToken, authSecret, promise, false);
}
@ReactMethod
public void signInAndRetrieveDataWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) {
signInWithCredential(appName, provider, authToken, authSecret, promise, true);
}
public void signInWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise, final boolean withData) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -626,7 +645,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:onComplete:success");
promiseWithUser(task.getResult().getUser(), promise);
if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
promiseWithUser(task.getResult().getUser(), promise);
}
} else {
Exception exception = task.getException();
Log.e(TAG, "signInWithCredential:onComplete:failure", exception);
@ -954,7 +977,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
/**
* link
* linkWithCredential
*
* @param provider
* @param authToken
@ -962,7 +985,16 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void link(String appName, String provider, String authToken, String authSecret, final Promise promise) {
public void linkWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) {
link(appName, provider, authToken, authSecret, promise, false);
}
@ReactMethod
public void linkAndRetrieveDataWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) {
link(appName, provider, authToken, authSecret, promise, true);
}
private void link(String appName, String provider, String authToken, String authSecret, final Promise promise, final boolean withData) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -981,7 +1013,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "link:onComplete:success");
promiseWithUser(task.getResult().getUser(), promise);
if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
promiseWithUser(task.getResult().getUser(), promise);
}
} else {
Exception exception = task.getException();
Log.e(TAG, "link:onComplete:failure", exception);
@ -995,13 +1031,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* unlink
*
* @param providerId
* @param promise
* @url https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseUser.html#unlink(java.lang.String)
*/
@ReactMethod
public void unlink(final String appName, final String providerId, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -1029,16 +1058,8 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* reauthenticate
*
* @param provider
* @param authToken
* @param authSecret
* @param promise
*/
@ReactMethod
public void reauthenticate(String appName, String provider, String authToken, String authSecret, final Promise promise) {
public void reauthenticateWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -1073,11 +1094,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
/**
* Returns an instance of AuthCredential for the specified provider
*
* @param provider
* @param authToken
* @param authSecret
* @return
*/
private AuthCredential getCredentialForProvider(String provider, String authToken, String authSecret) {
switch (provider) {
@ -1089,6 +1105,8 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
return TwitterAuthProvider.getCredential(authToken, authSecret);
case "github.com":
return GithubAuthProvider.getCredential(authToken);
case "oauth":
return OAuthProvider.getCredential(provider, authToken, authSecret);
case "phone":
// If the phone number is auto-verified quickly, then the verificationId can be null
// We cached the credential as part of the verifyPhoneNumber request to be re-used here
@ -1107,11 +1125,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* getToken
*
* @param promise
*/
@ReactMethod
public void getToken(String appName, Boolean forceRefresh, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -1140,11 +1153,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* fetchProvidersForEmail
*
* @param promise
*/
@ReactMethod
public void fetchProvidersForEmail(String appName, String email, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -1177,6 +1185,44 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
});
}
@ReactMethod
public void setLanguageCode(String appName, String code) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.setLanguageCode(code);
}
@ReactMethod
public void useDeviceLanguage(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.useAppLanguage();
}
@ReactMethod
public void verifyPasswordResetCode(String appName, String code, final Promise promise) {
Log.d(TAG, "verifyPasswordResetCode");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.verifyPasswordResetCode(code).addOnCompleteListener(new OnCompleteListener<String>() {
@Override
public void onComplete(@NonNull Task<String> task) {
if (task.isSuccessful()) {
Log.d(TAG, "verifyPasswordResetCode:onComplete:success");
promise.resolve(task.getResult());
} else {
Exception exception = task.getException();
Log.e(TAG, "verifyPasswordResetCode:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
}
});
}
/* ------------------
* INTERNAL HELPERS
* ---------------- */
@ -1210,6 +1256,97 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* promiseWithAuthResult
*
* @param authResult
* @param promise
*/
private void promiseWithAuthResult(AuthResult authResult, Promise promise) {
if (authResult != null && authResult.getUser() != null) {
WritableMap userMap = firebaseUserToMap(authResult.getUser());
WritableMap authResultMap = Arguments.createMap();
if (authResult.getAdditionalUserInfo() != null) {
WritableMap additionalUserInfoMap = Arguments.createMap();
additionalUserInfoMap.putBoolean("isNewUser", authResult.getAdditionalUserInfo().isNewUser());
if (authResult.getAdditionalUserInfo().getProfile() != null) {
WritableMap profileMap = mapToWritableMap(authResult.getAdditionalUserInfo().getProfile());
additionalUserInfoMap.putMap("profile", profileMap);
}
if (authResult.getAdditionalUserInfo().getProviderId() != null) {
additionalUserInfoMap.putString("providerId", authResult.getAdditionalUserInfo().getProviderId());
}
if (authResult.getAdditionalUserInfo().getUsername() != null) {
additionalUserInfoMap.putString("username", authResult.getAdditionalUserInfo().getUsername());
}
authResultMap.putMap("additionalUserInfo", additionalUserInfoMap);
}
authResultMap.putMap("user", userMap);
promise.resolve(authResultMap);
} else {
promiseNoUser(promise, true);
}
}
private WritableMap mapToWritableMap(Map<String, Object> map) {
WritableMap writableMap = Arguments.createMap();
for (String key : map.keySet()) {
Object value = map.get(key);
if (value == null) {
writableMap.putNull(key);
} else if (value instanceof Boolean) {
writableMap.putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
writableMap.putDouble(key, ((Integer) value).doubleValue());
} else if (value instanceof Long) {
writableMap.putDouble(key, ((Long) value).doubleValue());
} else if (value instanceof Double) {
writableMap.putDouble(key, (Double) value);
} else if (value instanceof Float) {
writableMap.putDouble(key, ((Float) value).doubleValue());
} else if (value instanceof String) {
writableMap.putString(key, (String) value);
} else if (Map.class.isAssignableFrom(value.getClass())) {
writableMap.putMap(key, mapToWritableMap((Map<String, Object>) value));
} else if (List.class.isAssignableFrom(value.getClass())) {
writableMap.putArray(key, listToWritableArray((List<Object>) value));
} else {
Log.e(TAG, "mapToWritableMap: Cannot convert object of type " + value.getClass());
}
}
return writableMap;
}
private WritableArray listToWritableArray(List<Object> list) {
WritableArray writableArray = Arguments.createArray();
for (Object item : list) {
if (item == null) {
writableArray.pushNull();
} else if (item instanceof Boolean) {
writableArray.pushBoolean((Boolean) item);
} else if (item instanceof Integer) {
writableArray.pushDouble(((Integer) item).doubleValue());
} else if (item instanceof Long) {
writableArray.pushDouble(((Long) item).doubleValue());
} else if (item instanceof Double) {
writableArray.pushDouble((Double) item);
} else if (item instanceof Float) {
writableArray.pushDouble(((Float) item).doubleValue());
} else if (item instanceof String) {
writableArray.pushString((String) item);
} else if (Map.class.isAssignableFrom(item.getClass())) {
writableArray.pushMap(mapToWritableMap((Map<String, Object>) item));
} else if (List.class.isAssignableFrom(item.getClass())) {
writableArray.pushArray(listToWritableArray((List<Object>) item));
} else {
Log.e(TAG, "listToWritableArray: Cannot convert object of type " + item.getClass());
}
}
return writableArray;
}
/**
* promiseRejectAuthException
*
@ -1410,6 +1547,14 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
userMap.putArray("providerData", convertProviderData(user.getProviderData(), user));
WritableMap metadataMap = Arguments.createMap();
FirebaseUserMetadata metadata = user.getMetadata();
if (metadata != null) {
metadataMap.putDouble("creationTime", metadata.getCreationTimestamp());
metadataMap.putDouble("lastSignInTime", metadata.getLastSignInTimestamp());
}
userMap.putMap("metadata", metadataMap);
return userMap;
}
@ -1451,4 +1596,29 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
eventMap.putMap("state", state);
Utils.sendEvent(mReactContext, "phone_auth_state_changed", eventMap);
}
/**
* Constants bootstrapped on react native app boot
*
* @return
*/
@Override
public Map<String, Object> getConstants() {
Map<String, Object> constants = new HashMap<>();
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
final Map<String, Object> appLanguage = new HashMap<>();
for (FirebaseApp app : firebaseAppList) {
String appName = app.getName();
FirebaseApp instance = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(instance);
appLanguage.put(appName, firebaseAuth.getLanguageCode());
}
constants.put("APP_LANGUAGE", appLanguage);
return constants;
}
}

View File

@ -12,6 +12,7 @@ import com.facebook.react.bridge.WritableMap;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FieldPath;
import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.GeoPoint;
@ -196,7 +197,7 @@ public class FirestoreSerialize {
} else if (Map.class.isAssignableFrom(value.getClass())) {
typeMap.putString("type", "object");
typeMap.putMap("value", objectMapToWritable((Map<String, Object>) value));
} else if (List.class.isAssignableFrom(value.getClass())) {
} else if (List.class.isAssignableFrom(value.getClass())) {
typeMap.putString("type", "array");
List<Object> list = (List<Object>) value;
Object[] array = list.toArray(new Object[list.size()]);
@ -214,7 +215,6 @@ public class FirestoreSerialize {
typeMap.putString("type", "date");
typeMap.putDouble("value", ((Date) value).getTime());
} else {
// TODO: Changed to log an error rather than crash - is this correct?
Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass());
typeMap.putString("type", "null");
typeMap.putNull("value");
@ -269,6 +269,8 @@ public class FirestoreSerialize {
} else if ("date".equals(type)) {
Double time = typeMap.getDouble("value");
return new Date(time.longValue());
} else if ("documentid".equals(type)) {
return FieldPath.documentId();
} else if ("fieldvalue".equals(type)) {
String value = typeMap.getString("value");
if ("delete".equals(value)) {

View File

@ -14,6 +14,7 @@ import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.DocumentListenOptions;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FieldPath;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.ListenerRegistration;
@ -21,6 +22,7 @@ import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QueryListenOptions;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -128,27 +130,56 @@ public class RNFirebaseFirestoreCollectionReference {
private Query applyFilters(FirebaseFirestore firestore, Query query) {
for (int i = 0; i < filters.size(); i++) {
ReadableMap filter = filters.getMap(i);
String fieldPath = filter.getString("fieldPath");
ReadableMap fieldPathMap = filter.getMap("fieldPath");
String fieldPathType = fieldPathMap.getString("type");
String operator = filter.getString("operator");
ReadableMap jsValue = filter.getMap("value");
Object value = FirestoreSerialize.parseTypeMap(firestore, jsValue);
switch (operator) {
case "EQUAL":
query = query.whereEqualTo(fieldPath, value);
break;
case "GREATER_THAN":
query = query.whereGreaterThan(fieldPath, value);
break;
case "GREATER_THAN_OR_EQUAL":
query = query.whereGreaterThanOrEqualTo(fieldPath, value);
break;
case "LESS_THAN":
query = query.whereLessThan(fieldPath, value);
break;
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
if (fieldPathType.equals("string")) {
String fieldPath = fieldPathMap.getString("string");
switch (operator) {
case "EQUAL":
query = query.whereEqualTo(fieldPath, value);
break;
case "GREATER_THAN":
query = query.whereGreaterThan(fieldPath, value);
break;
case "GREATER_THAN_OR_EQUAL":
query = query.whereGreaterThanOrEqualTo(fieldPath, value);
break;
case "LESS_THAN":
query = query.whereLessThan(fieldPath, value);
break;
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
}
} else {
ReadableArray fieldPathElements = fieldPathMap.getArray("elements");
String[] fieldPathArray = new String[fieldPathElements.size()];
for (int j=0; j<fieldPathElements.size(); j++) {
fieldPathArray[j] = fieldPathElements.getString(j);
}
FieldPath fieldPath = FieldPath.of(fieldPathArray);
switch (operator) {
case "EQUAL":
query = query.whereEqualTo(fieldPath, value);
break;
case "GREATER_THAN":
query = query.whereGreaterThan(fieldPath, value);
break;
case "GREATER_THAN_OR_EQUAL":
query = query.whereGreaterThanOrEqualTo(fieldPath, value);
break;
case "LESS_THAN":
query = query.whereLessThan(fieldPath, value);
break;
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
}
}
}
return query;
@ -159,9 +190,17 @@ public class RNFirebaseFirestoreCollectionReference {
for (Object o : ordersList) {
Map<String, Object> order = (Map) o;
String direction = (String) order.get("direction");
String fieldPath = (String) order.get("fieldPath");
Map<String, Object> fieldPathMap = (Map) order.get("fieldPath");
String fieldPathType = (String)fieldPathMap.get("type");
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
if (fieldPathType.equals("string")) {
String fieldPath = (String)fieldPathMap.get("string");
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
} else {
List<String> fieldPathElements = (List)fieldPathMap.get("elements");
FieldPath fieldPath = FieldPath.of(fieldPathElements.toArray(new String[fieldPathElements.size()]));
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
}
}
return query;
}

View File

@ -453,31 +453,15 @@
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseStorage/Frameworks",
"${SRCROOT}/../../../ios/Pods/Google-Mobile-Ads-SDK/Frameworks/frameworks",
"${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/**",
"${SRCROOT}/../../../ios/Firebase/**",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
@ -498,31 +482,15 @@
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseStorage/Frameworks",
"${SRCROOT}/../../../ios/Pods/Google-Mobile-Ads-SDK/Frameworks/frameworks",
"${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/**",
"${SRCROOT}/../../../ios/Firebase/**",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";

View File

@ -3,7 +3,7 @@
#import <Foundation/Foundation.h>
static NSString *const AUTH_CHANGED_EVENT = @"auth_state_changed";
static NSString *const AUTH_STATE_CHANGED_EVENT = @"auth_state_changed";
static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed";

View File

@ -29,9 +29,9 @@ RCT_EXPORT_METHOD(addAuthStateListener:
if (![_authStateHandlers valueForKey:firApp.name]) {
FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_STATE_CHANGED_EVENT body:@{@"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
}
}];
@ -64,9 +64,9 @@ RCT_EXPORT_METHOD(addIdTokenListener:
if (![_idTokenHandlers valueForKey:firApp.name]) {
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp 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:@{@"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{}];
}
}];
@ -125,12 +125,9 @@ RCT_EXPORT_METHOD(signOut:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInAnonymously:
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(signInAnonymously:(NSString *) appDisplayName
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
@ -140,7 +137,27 @@ RCT_EXPORT_METHOD(signInAnonymously:
[self promiseWithUser:resolve rejecter:reject user:user];
}
}];
}
/**
signInAnonymouslyAndRetrieveData
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInAnonymouslyAndRetrieveData:(NSString *) appDisplayName
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyAndRetrieveDataWithCompletion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
/**
@ -152,16 +169,11 @@ RCT_EXPORT_METHOD(signInAnonymously:
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(NSString *) appDisplayName
email:
(NSString *) email
pass:
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *) appDisplayName
email:(NSString *) email
pass:(NSString *) password
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
@ -173,6 +185,31 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
}];
}
/**
signInAndRetrieveDataWithEmailAndPassword
@param NSString NSString email
@param NSString NSString password
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(signInAndRetrieveDataWithEmailAndPassword:(NSString *) appDisplayName
email:(NSString *) email
pass:(NSString *) password
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithEmail:email password:password completion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
/**
createUserWithEmailAndPassword
@ -182,16 +219,11 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(NSString *) appDisplayName
email:
(NSString *) email
pass:
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *) appDisplayName
email:(NSString *) email
pass:(NSString *) password
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
@ -203,6 +235,32 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
}];
}
/**
createUserAndRetrieveDataWithEmailAndPassword
@param NSString NSString email
@param NSString NSString password
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(createUserAndRetrieveDataWithEmailAndPassword:(NSString *) appDisplayName
email:(NSString *) email
pass:(NSString *) password
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserAndRetrieveDataWithEmail:email password:password
completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
/**
deleteUser
@ -447,18 +505,12 @@ RCT_EXPORT_METHOD(getToken:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInWithCredential:
(NSString *) appDisplayName
provider:
(NSString *) provider
token:
(NSString *) authToken
secret:
(NSString *) authSecret
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(signInWithCredential:(NSString *) appDisplayName
provider:(NSString *) provider
token:(NSString *) authToken
secret:(NSString *) authSecret
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -476,6 +528,39 @@ RCT_EXPORT_METHOD(signInWithCredential:
}];
}
/**
signInAndRetrieveDataWithCredential
@param NSString provider
@param NSString authToken
@param NSString authSecret
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInAndRetrieveDataWithCredential:(NSString *) appDisplayName
provider:(NSString *) provider
token:(NSString *) authToken
secret:(NSString *) authSecret
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil);
}
[[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
/**
confirmPasswordReset
@ -610,43 +695,42 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
}
}
/**
getCurrentUser
signInAndRetrieveDataWithCustomToken
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(getCurrentUser:
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(signInAndRetrieveDataWithCustomToken:(NSString *) appDisplayName
customToken:(NSString *) customToken
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:user];
[[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCustomToken:customToken completion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
/**
signInWithCustomToken
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInWithCustomToken:
(NSString *) appDisplayName
customToken:
(NSString *) customToken
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(signInWithCustomToken:(NSString *) appDisplayName
customToken:(NSString *) customToken
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
@ -737,7 +821,7 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
}
/**
link - *insert zelda joke here*
linkWithCredential
@param NSString provider
@param NSString authToken
@ -746,20 +830,13 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(link:
(NSString *) appDisplayName
provider:
(NSString *) provider
authToken:
(NSString *) authToken
authSecret:
(NSString *) authSecret
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
RCT_EXPORT_METHOD(linkWithCredential:(NSString *) appDisplayName
provider:(NSString *) provider
authToken:(NSString *) authToken
authSecret:(NSString *) authSecret
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
@ -781,6 +858,44 @@ RCT_EXPORT_METHOD(link:
}
}
/**
linkAndRetrieveDataWithCredential
@param NSString provider
@param NSString authToken
@param NSString authSecret
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(linkAndRetrieveDataWithCredential:(NSString *) appDisplayName
provider:(NSString *) provider
authToken:(NSString *) authToken
authSecret:(NSString *) authSecret
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil);
}
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
[user linkAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}
/**
unlink
@ -816,7 +931,7 @@ RCT_EXPORT_METHOD(unlink:
}
/**
reauthenticate
reauthenticateWithCredential
@param NSString provider
@param NSString authToken
@ -825,7 +940,7 @@ RCT_EXPORT_METHOD(unlink:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(reauthenticate:
RCT_EXPORT_METHOD(reauthenticateWithCredential:
(NSString *) appDisplayName
provider:
(NSString *) provider
@ -914,6 +1029,8 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
credential = [FIRGitHubAuthProvider credentialWithToken:authToken];
} else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret];
} else if ([provider compare:@"oauth" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIROAuthProvider credentialWithProviderID:@"oauth" IDToken:authToken accessToken:authTokenSecret];
} else {
NSLog(@"Provider not yet handled: %@", provider);
}
@ -921,6 +1038,47 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
return credential;
}
/**
setLanguageCode
@param NSString code
@return
*/
RCT_EXPORT_METHOD(setLanguageCode:
(NSString *) appDisplayName
code:
(NSString *) code) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[FIRAuth authWithApp:firApp].languageCode = code;
}
/**
useDeviceLanguage
@param NSString code
@return
*/
RCT_EXPORT_METHOD(useDeviceLanguage:(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] useAppLanguage];
}
RCT_EXPORT_METHOD(verifyPasswordResetCode:(NSString *) appDisplayName
code:(NSString *)code
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] verifyPasswordResetCode:code completion:^(NSString * _Nullable email, NSError * _Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
resolve(email);
}
}];
}
// This is here to protect against bugs in the iOS SDK which don't
// correctly refresh the user object when performing certain operations
- (void)reloadAndReturnUser:(FIRUser *)user
@ -1105,6 +1263,32 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
}
/**
Resolve or reject a promise based on FIRAuthResult value existance
@param resolve RCTPromiseResolveBlock
@param reject RCTPromiseRejectBlock
@param authResult FIRAuthDataResult
*/
- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject authResult:(FIRAuthDataResult *)authResult {
if (authResult && authResult.user) {
NSDictionary *userDict = [self firebaseUserToDict:authResult.user];
NSDictionary *authResultDict = @{
@"additionalUserInfo": authResult.additionalUserInfo ? @{
@"isNewUser": @(authResult.additionalUserInfo.isNewUser),
@"profile": authResult.additionalUserInfo.profile ? authResult.additionalUserInfo.profile : [NSNull null],
@"providerId": authResult.additionalUserInfo.providerID ? authResult.additionalUserInfo.providerID : [NSNull null],
@"username": authResult.additionalUserInfo.username ? authResult.additionalUserInfo.username : [NSNull null]
} : [NSNull null],
@"user": userDict
};
resolve(authResultDict);
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}
/**
Converts an array of FIRUserInfo instances into the correct format to match the web sdk
@ -1147,20 +1331,48 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
return output;
}
/**
* React native constant exports - exports native firebase apps mainly
* @return NSDictionary
*/
- (NSDictionary *)constantsToExport {
NSMutableDictionary *constants = [NSMutableDictionary new];
NSDictionary *firApps = [FIRApp allApps];
NSMutableDictionary *appLanguage = [NSMutableDictionary new];
for (id key in firApps) {
FIRApp *firApp = firApps[key];
appLanguage[firApp.name] = [FIRAuth authWithApp:firApp].languageCode;
}
constants[@"APP_LANGUAGE"] = appLanguage;
return constants;
}
/**
Converts a FIRUser instance into a dictionary to send via RNBridge
@param user FIRUser
@return NSDictionary
*/
- (NSMutableDictionary *)firebaseUserToDict:(FIRUser *)user {
NSMutableDictionary *userDict = [@{@"uid": user.uid, @"email": user.email ? user.email : [NSNull null], @"emailVerified": @(user.emailVerified), @"isAnonymous": @(user.anonymous), @"displayName": user.displayName ? user.displayName : [NSNull null], @"refreshToken": user.refreshToken, @"providerId": [user.providerID lowercaseString], @"phoneNumber": user.phoneNumber ? user.phoneNumber : [NSNull null], @"providerData": [self convertProviderData:user.providerData]} mutableCopy];
if ([user valueForKey:@"photoURL"] != nil) {
[userDict setValue:[user.photoURL absoluteString] forKey:@"photoURL"];
}
return userDict;
- (NSDictionary *)firebaseUserToDict:(FIRUser *)user {
return @{
@"displayName": user.displayName ? user.displayName : [NSNull null],
@"email": user.email ? user.email : [NSNull null],
@"emailVerified": @(user.emailVerified),
@"isAnonymous": @(user.anonymous),
@"metadata": @{
@"creationTime": user.metadata.creationDate ? @(round([user.metadata.creationDate timeIntervalSince1970] * 1000.0)): [NSNull null],
@"lastSignInTime": user.metadata.lastSignInDate ? @(round([user.metadata.lastSignInDate timeIntervalSince1970] * 1000.0)) : [NSNull null],
},
@"phoneNumber": user.phoneNumber ? user.phoneNumber : [NSNull null],
@"photoURL": user.photoURL ? [user.photoURL absoluteString] : [NSNull null],
@"providerData": [self convertProviderData:user.providerData],
@"providerId": [user.providerID lowercaseString],
@"refreshToken": user.refreshToken,
@"uid": user.uid
};
}
- (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSettings {
@ -1188,7 +1400,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
}
- (NSArray<NSString *> *)supportedEvents {
return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT];
return @[AUTH_STATE_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT];
}
+ (BOOL)requiresMainQueueSetup

View File

@ -93,21 +93,39 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (FIRQuery *)applyFilters:(FIRFirestore *) firestore
query:(FIRQuery *) query {
for (NSDictionary *filter in _filters) {
NSString *fieldPath = filter[@"fieldPath"];
NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
NSString *fieldPathType = fieldPathDictionary[@"type"];
NSString *operator = filter[@"operator"];
NSDictionary *jsValue = filter[@"value"];
id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue];
if ([operator isEqualToString:@"EQUAL"]) {
query = [query queryWhereField:fieldPath isEqualTo:value];
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
query = [query queryWhereField:fieldPath isGreaterThan:value];
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
} else if ([operator isEqualToString:@"LESS_THAN"]) {
query = [query queryWhereField:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
if ([fieldPathType isEqualToString:@"string"]) {
NSString *fieldPath = fieldPathDictionary[@"string"];
if ([operator isEqualToString:@"EQUAL"]) {
query = [query queryWhereField:fieldPath isEqualTo:value];
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
query = [query queryWhereField:fieldPath isGreaterThan:value];
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
} else if ([operator isEqualToString:@"LESS_THAN"]) {
query = [query queryWhereField:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
}
} else {
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
if ([operator isEqualToString:@"EQUAL"]) {
query = [query queryWhereFieldPath:fieldPath isEqualTo:value];
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
query = [query queryWhereFieldPath:fieldPath isGreaterThan:value];
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value];
} else if ([operator isEqualToString:@"LESS_THAN"]) {
query = [query queryWhereFieldPath:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value];
}
}
}
return query;
@ -116,9 +134,17 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (FIRQuery *)applyOrders:(FIRQuery *) query {
for (NSDictionary *order in _orders) {
NSString *direction = order[@"direction"];
NSString *fieldPath = order[@"fieldPath"];
NSDictionary *fieldPathDictionary = order[@"fieldPath"];
NSString *fieldPathType = fieldPathDictionary[@"type"];
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
if ([fieldPathType isEqualToString:@"string"]) {
NSString *fieldPath = fieldPathDictionary[@"string"];
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
} else {
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
query = [query queryOrderedByFieldPath:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
}
}
return query;
}

View File

@ -255,6 +255,8 @@ static NSMutableDictionary *_listeners;
return [[FIRGeoPoint alloc] initWithLatitude:[latitude doubleValue] longitude:[longitude doubleValue]];
} else if ([type isEqualToString:@"date"]) {
return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *)value doubleValue] / 1000.0)];
} else if ([type isEqualToString:@"documentid"]) {
return [FIRFieldPath documentID];
} else if ([type isEqualToString:@"fieldvalue"]) {
NSString *string = (NSString*)value;
if ([string isEqualToString:@"delete"]) {

View File

@ -6,10 +6,12 @@
static void sendDynamicLink(NSURL *url, id sender) {
[[NSNotificationCenter defaultCenter] postNotificationName:LINKS_DYNAMIC_LINK_RECEIVED
object:sender
userInfo:@{@"url": url.absoluteString}];
NSLog(@"sendDynamicLink Success: %@", url.absoluteString);
if (url) {
[[NSNotificationCenter defaultCenter] postNotificationName:LINKS_DYNAMIC_LINK_RECEIVED
object:sender
userInfo:@{@"url": url.absoluteString}];
NSLog(@"sendDynamicLink Success: %@", url.absoluteString);
}
}
@implementation RNFirebaseLinks
@ -161,9 +163,11 @@ RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RC
NSLog(@"create short dynamic link failure %@", [error localizedDescription]);
reject(@"links/failure", @"Failed to create Short Dynamic Link", error);
}
NSURL *shortLink = shortURL;
NSLog(@"created short dynamic link: %@", shortLink.absoluteString);
resolve(shortLink.absoluteString);
else {
NSURL *shortLink = shortURL;
NSLog(@"created short dynamic link: %@", shortLink.absoluteString);
resolve(shortLink.absoluteString);
}
}];
}
@catch(NSException * e) {

View File

@ -45,6 +45,12 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
content.userInfo = details;
content.badge = [RCTConvert NSNumber:details[@"badge"]];
if([details objectForKey:@"show_in_foreground"] != nil) {
if([(NSNumber *)details[@"show_in_foreground"] boolValue] == YES) {
[content setValue:@YES forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"];
}
}
NSDate *fireDate = [RCTConvert NSDate:details[@"fire_date"]];
if(fireDate == nil){

View File

@ -162,7 +162,7 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appDisplayName
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxDownloadRetryTime:[milliseconds doubleValue]];
}
@ -174,7 +174,7 @@ RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appDisplayName
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxOperationRetryTime:[milliseconds doubleValue]];
}
@ -185,7 +185,7 @@ RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appDisplayName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
*/
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
milliseconds:(nonnull NSNumber *) milliseconds) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxUploadRetryTime:[milliseconds doubleValue]];
}

View File

@ -1,9 +1,7 @@
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules"
]
}
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true
},
"exclude": ["node_modules"]
}

693
lib/index.d.ts vendored
View File

@ -5,80 +5,77 @@
declare module "react-native-firebase" {
type AuthProvider = {
PROVIDER_ID: string,
credential: (token: string, secret?: string) => object,
};
export default class FireBase {
constructor(config?: RNFirebase.configurationOptions)
log: any;
analytics(): RNFirebase.Analytics;
on(type: string, handler: (msg: any) => void): any;
database: {
(): RNFirebase.database.Database
ServerValue: {
TIMESTAMP: number
}
};
auth: {
(): RNFirebase.auth.Auth
EmailAuthProvider: AuthProvider,
PhoneAuthProvider: AuthProvider,
GoogleAuthProvider: AuthProvider,
GithubAuthProvider: AuthProvider,
TwitterAuthProvider: AuthProvider,
FacebookAuthProvider: AuthProvider,
PhoneAuthState: {
CODE_SENT: string,
AUTO_VERIFY_TIMEOUT: string,
AUTO_VERIFIED: string,
ERROR: string,
},
};
/**RNFirebase mimics the Web Firebase SDK Storage,
* whilst providing some iOS and Android specific functionality.
*/
storage(): RNFirebase.storage.Storage;
/**
* Firebase Cloud Messaging (FCM) allows you to send push messages at no cost to both Android & iOS platforms.
* Assuming the installation instructions have been followed, FCM is ready to go.
* As the Firebase Web SDK has limited messaging functionality,
* the following methods within react-native-firebase have been created to handle FCM in the React Native environment.
*/
messaging(): RNFirebase.messaging.Messaging;
/**
* RNFirebase provides crash reporting for your app out of the box.
* Please note crashes do not appear in real-time on the console,
* they tend to take a number of hours to appear
* If you want to manually report a crash,
* such as a pre-caught exception this is possible by using the report method.
*/
crash(): RNFirebase.crash.Crash;
static fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics;
};
apps: Array<string>;
googleApiAvailability: RNFirebase.GoogleApiAvailabilityType;
static initializeApp(options?: any | RNFirebase.configurationOptions, name?: string): FireBase;
static app(name?: string): FireBase;
[key: string]: any;
/** 3rd party provider Credentials */
type AuthCredential = {
providerId: string,
token: string,
secret: string
}
namespace RNFirebase {
type FirebaseModuleAndStatics<M, S = {}> = {
(): M;
nativeModuleExists: boolean;
} & S
// Modules commented-out do not currently have type definitions
export class Firebase {
private constructor();
// admob: FirebaseModuleAndStatics<RNFirebase.admob.AdMob>;
analytics: FirebaseModuleAndStatics<RNFirebase.Analytics>;
auth: FirebaseModuleAndStatics<RNFirebase.auth.Auth, RNFirebase.auth.AuthStatics>;
// config: FirebaseModule<RNFirebase.config.Config>;
crash: FirebaseModuleAndStatics<RNFirebase.crash.Crash>;
database: FirebaseModuleAndStatics<RNFirebase.database.Database, RNFirebase.database.DatabaseStatics>;
fabric: {
crashlytics: FirebaseModuleAndStatics<RNFirebase.crashlytics.Crashlytics>;
};
firestore: FirebaseModuleAndStatics<RNFirebase.firestore.Firestore, RNFirebase.firestore.FirestoreStatics>;
links: FirebaseModuleAndStatics<RNFirebase.links.Links>;
messaging: FirebaseModuleAndStatics<RNFirebase.messaging.Messaging>;
// perf: FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
storage: FirebaseModuleAndStatics<RNFirebase.storage.Storage>;
// utils: FirebaseModuleAndStatics<RNFirebase.utils.Utils>;
initializeApp(options: Firebase.Options, name: string): App;
app(name?: string): App;
readonly apps: App[];
readonly SDK_VERSION: string;
}
namespace Firebase {
interface Options {
apiKey: string;
appId: string;
databaseURL: string;
messagingSenderId: string;
projectId: string;
storageBucket: string;
}
}
const firebase: Firebase;
export default firebase;
// Modules commented-out do not currently have type definitions
export class App {
private constructor();
// admob(): RNFirebase.admob.AdMob;
analytics(): RNFirebase.Analytics;
auth(): RNFirebase.auth.Auth;
// config(): RNFirebase.config.Config;
crash(): RNFirebase.crash.Crash;
database(): RNFirebase.database.Database;
fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics,
};
firestore(): RNFirebase.firestore.Firestore;
links(): RNFirebase.links.Links;
messaging(): RNFirebase.messaging.Messaging;
// perf(): RNFirebase.perf.Performance;
storage(): RNFirebase.storage.Storage;
// utils(): RNFirebase.utils.Utils;
readonly name: string;
readonly options: Firebase.Options;
}
export namespace RNFirebase {
interface RnError extends Error {
code?: string;
}
@ -476,6 +473,15 @@ declare module "react-native-firebase" {
update(values: Object, onComplete?: (a: RnError | null) => any): Promise<any>;
}
interface DatabaseStatics {
/** @see https://www.firebase.com/docs/java-api/javadoc/com/firebase/client/ServerValue.html#TIMESTAMP */
ServerValue: {
TIMESTAMP: {
[key: string]: string
}
}
}
}
/**
@ -525,6 +531,37 @@ declare module "react-native-firebase" {
[key: string]: any;
}
type AdditionalUserInfo = {
isNewUser: boolean,
profile?: Object,
providerId: string,
username?: string,
}
type UserCredential = {
additionalUserInfo?: AdditionalUserInfo,
user: User,
}
type UserInfo = {
displayName?: string,
email?: string,
phoneNumber?: string,
photoURL?: string,
providerId: string,
uid: string,
}
type UpdateProfile = {
displayName?: string,
photoURL?: string,
}
type UserMetadata = {
creationTime?: string,
lastSignInTime?: string,
}
interface User {
/**
* The user's display name (if available).
@ -542,6 +579,10 @@ declare module "react-native-firebase" {
*
*/
isAnonymous: boolean
metadata: UserMetadata
phoneNumber: string | null
/**
* - The URL of the user's profile picture (if available).
*/
@ -549,12 +590,12 @@ declare module "react-native-firebase" {
/**
* - Additional provider-specific information about the user.
*/
providerData: any | null
providerData: Array<UserInfo>
/**
* - The authentication provider ID for the current user.
* For example, 'facebook.com', or 'google.com'.
*/
providerId: string | null
providerId: string
/**
* - The user's unique ID.
*/
@ -567,18 +608,26 @@ declare module "react-native-firebase" {
/**
* Returns the users authentication token.
*
* @param forceRefresh: boolean - default to false
*/
getToken(): Promise<string>
getIdToken(forceRefresh?: boolean): Promise<string>
/**
* Reauthenticate the current user with credentials:
*/
reauthenticate(credential: Credential): Promise<void>
getToken(forceRefresh?: boolean): Promise<string>
linkAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential>
/**
* Link the user with a 3rd party credential provider.
*/
linkWithCredential(credential: Credential): Promise<User>
linkWithCredential(credential: AuthCredential): Promise<User>
reauthenticateAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential>
/**
* Re-authenticate a user with a third-party authentication provider
*/
reauthenticateWithCredential(credential: AuthCredential): Promise<void>
/**
* Refreshes the current user.
@ -589,7 +638,11 @@ declare module "react-native-firebase" {
* Sends a verification email to a user.
* This will Promise reject is the user is anonymous.
*/
sendEmailVerification(): Promise<void>
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void>
toJSON(): object
unlink(providerId: string): Promise<User>
/**
* Updates the user's email address.
@ -609,57 +662,114 @@ declare module "react-native-firebase" {
* Updates a user's profile data.
* Profile data should be an object of fields to update:
*/
updateProfile(profile: Object): Promise<void>
updateProfile(updates: UpdateProfile): Promise<void>
}
/** 3rd party provider Credentials */
interface Credential {
provider: string,
token: string,
secret: string
type ActionCodeSettings = {
android: {
installApp?: boolean,
minimumVersion?: string,
packageName: string,
},
handleCodeInApp?: boolean,
iOS: {
bundleId?: string,
},
url: string,
}
interface ActionCodeInfo {
email: string,
error: string,
fromEmail: string,
verifyEmail: string,
recoverEmail: string,
passwordReset: string
data: {
email?: string,
fromEmail?: string
},
operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL'
}
interface ConfirmationResult {
confirm(verificationCode: string): Promise<User | null>;
verificationId: string | null;
}
type PhoneAuthSnapshot = {
state: 'sent' | 'timeout' | 'verified' | 'error',
verificationId: string,
code: string | null,
error: Error | null,
};
type PhoneAuthError = {
code: string | null,
verificationId: string,
message: string | null,
stack: string | null,
};
interface PhoneAuthListener {
on(event: string,
observer: () => PhoneAuthSnapshot,
errorCb?: () => PhoneAuthError,
successCb?: () => PhoneAuthSnapshot): PhoneAuthListener;
then(fn: () => PhoneAuthSnapshot): Promise<any>
catch(fn: () => Error): Promise<any>
}
namespace auth {
type AuthResult = {
authenticated: boolean,
user: object | null
} | null;
type AuthProvider = {
PROVIDER_ID: string,
credential: (token: string, secret?: string) => AuthCredential,
};
interface Auth {
readonly app: App;
/**
* Returns the current Firebase authentication state.
*/
authenticated: boolean;
authResult: AuthResult | null;
/**
* Returns the currently signed-in user (or null). See the User class documentation for further usage.
*/
currentUser: User | null
/**
* Gets/Sets the language for the app instance
*/
languageCode: string | null;
/**
* Listen for changes in the users auth state (logging in and out).
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
onAuthStateChanged(nextOrObserver: Object, error?: (a: RnError) => any,
completed?: () => any): () => any;
onAuthStateChanged(listener: Function): () => void;
/**
* We can create a user by calling the createUserWithEmailAndPassword() function.
* The method accepts two parameters, an email and a password.
* Listen for changes in id token.
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
createUserWithEmailAndPassword(email: string, password: string): Promise<User>
onIdTokenChanged(listener: Function): () => void;
/**
* To sign a user in with their email and password, use the signInWithEmailAndPassword() function.
* It accepts two parameters, the user's email and password:
* Listen for changes in the user.
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
signInWithEmailAndPassword(email: string, password: string): Promise<User>
onUserChanged(listener: Function): () => void;
signOut(): Promise<void>
signInAnonymouslyAndRetrieveData(): Promise<UserCredential>
/**
* Sign an anonymous user.
@ -667,11 +777,23 @@ declare module "react-native-firebase" {
*/
signInAnonymously(): Promise<User>
createUserAndRetrieveDataWithEmailAndPassword(email: string, password: string): Promise<UserCredential>
/**
* Sign in the user with a 3rd party credential provider.
* credential requires the following properties:
* We can create a user by calling the createUserWithEmailAndPassword() function.
* The method accepts two parameters, an email and a password.
*/
signInWithCredential(credential: Credential): Promise<User>
createUserWithEmailAndPassword(email: string, password: string): Promise<User>
signInAndRetrieveDataWithEmailAndPassword(email: string, password: string): Promise<UserCredential>
/**
* To sign a user in with their email and password, use the signInWithEmailAndPassword() function.
* It accepts two parameters, the user's email and password:
*/
signInWithEmailAndPassword(email: string, password: string): Promise<User>
signInAndRetrieveDataWithCustomToken(token: string): Promise<UserCredential>
/**
* Sign a user in with a self-signed JWT token.
@ -681,22 +803,42 @@ declare module "react-native-firebase" {
*/
signInWithCustomToken(token: string): Promise<User>
signInAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential>
/**
* Sign in the user with a 3rd party credential provider.
* credential requires the following properties:
*/
signInWithCredential(credential: AuthCredential): Promise<User>
/**
* Asynchronously signs in using a phone number.
*/
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult>
/**
* Returns a PhoneAuthListener to listen to phone verification events,
* on the final completion event a PhoneAuthCredential can be generated for
* authentication purposes.
*/
verifyPhoneNumber(phoneNumber: string, autoVerifyTimeout?: number): PhoneAuthListener
/**
* Sends a password reset email to the given email address.
* Unlike the web SDK,
* the email will contain a password reset link rather than a code.
*/
sendPasswordResetEmail(email: string): Promise<void>
sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void>
/**
* Completes the password reset process, given a confirmation code and new password.
*/
confirmPasswordReset(code: string, newPassword: string): Promise<any>
confirmPasswordReset(code: string, newPassword: string): Promise<void>
/**
* Applies a verification code sent to the user by email or other out-of-band mechanism.
*/
applyActionCode(code: string): Promise<any>
applyActionCode(code: string): Promise<void>
/**
* Checks a verification code sent to the user by email or other out-of-band mechanism.
@ -704,13 +846,30 @@ declare module "react-native-firebase" {
checkActionCode(code: string): Promise<ActionCodeInfo>
/**
* Completes the password reset process,
* given a confirmation code and new password.
* Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
*/
signOut(): Promise<void>
fetchProvidersForEmail(email: string): Promise<Array<string>>
verifyPasswordResetCode(code: string): Promise<string>
[key: string]: any;
}
interface AuthStatics {
EmailAuthProvider: AuthProvider;
PhoneAuthProvider: AuthProvider;
GoogleAuthProvider: AuthProvider;
GithubAuthProvider: AuthProvider;
OAuthProvider: AuthProvider;
TwitterAuthProvider: AuthProvider;
FacebookAuthProvider: AuthProvider;
PhoneAuthState: {
CODE_SENT: string;
AUTO_VERIFY_TIMEOUT: string;
AUTO_VERIFIED: string;
ERROR: string;
};
}
}
namespace messaging {
@ -895,5 +1054,315 @@ declare module "react-native-firebase" {
setUserIdentifier(userId: string): void;
}
}
namespace links {
interface Links {
/** Creates a standard dynamic link. */
createDynamicLink(parameters: LinkConfiguration): Promise<string>;
/** Creates a short dynamic link. */
createShortDynamicLink(parameters: LinkConfiguration): Promise<string>;
/**
* Returns the URL that the app has been launched from. If the app was
* not launched from a URL the return value will be null.
*/
getInitialLink(): Promise<string | null>;
/**
* Subscribe to URL open events while the app is still running.
* The listener is called from URL open events whilst the app is still
* running, use getInitialLink for URLs which cause the app to open
* from a previously closed / not running state.
* Returns an unsubscribe function, call the returned function to
* unsubscribe from all future events.
*/
onLink(listener: (url: string) => void): () => void;
}
/**
* Configuration when creating a Dynamic Link (standard or short). For
* more information about each parameter, see the official Firebase docs:
* https://firebase.google.com/docs/reference/dynamic-links/link-shortener
*/
interface LinkConfiguration {
link: string,
dynamicLinkDomain: string,
androidInfo?: {
androidLink?: string,
androidPackageName: string,
androidFallbackLink?: string,
androidMinPackageVersionCode?: string,
},
iosInfo?: {
iosBundleId: string,
iosAppStoreId?: string,
iosFallbackLink?: string,
iosCustomScheme?: string,
iosIpadBundleId?: string,
iosIpadFallbackLink?: string,
},
socialMetaTagInfo?: {
socialTitle: string,
socialImageLink: string,
socialDescription: string,
},
suffix?: {
option: 'SHORT' | 'UNGUESSABLE',
},
}
}
namespace firestore {
interface Firestore {
readonly app: App;
batch(): WriteBatch;
collection(collectionPath: string): CollectionReference;
doc(documentPath: string): DocumentReference;
/** NOT SUPPORTED YET */
// enablePersistence(): Promise<void>;
/** NOT SUPPORTED YET */
// runTransaction(): Promise<any>;
/** NOT SUPPORTED YET */
// settings(): void;
}
interface FirestoreStatics {
FieldPath: typeof FieldPath;
FieldValue: typeof FieldValue;
GeoPoint: typeof GeoPoint;
enableLogging(enabled: boolean): void;
}
interface CollectionReference {
readonly firestore: Firestore;
readonly id: string;
readonly parent: DocumentReference;
add(data: object): Promise<DocumentReference>;
doc(documentPath?: string): DocumentReference;
endAt(snapshot: DocumentSnapshot): Query;
endAt(...varargs: any[]): Query;
endBefore(snapshot: DocumentSnapshot): Query;
endBefore(...varargs: any[]): Query;
get(): Promise<QuerySnapshot>;
limit(limit: number): Query;
onSnapshot(onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(observer: Query.Observer): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, observer: Query.Observer): () => void;
orderBy(fieldPath: string | FieldPath, directionStr?: Types.QueryDirection): Query;
startAfter(snapshot: DocumentSnapshot): Query;
startAfter(...varargs: any[]): Query;
startAt(snapshot: DocumentSnapshot): Query;
startAt(...varargs: any[]): Query;
where(fieldPath: string, op: Types.QueryOperator, value: any): Query;
}
interface DocumentChange {
readonly doc: DocumentSnapshot;
readonly newIndex: number;
readonly oldIndex: number;
readonly type: string;
}
interface DocumentReference {
readonly firestore: Firestore;
readonly id: string | null;
readonly parent: CollectionReference;
readonly path: string;
collection(collectionPath: string): CollectionReference;
delete(): Promise<void>;
get(): Promise<DocumentSnapshot>;
onSnapshot(onNext: DocumentReference.ObserverOnNext, onError?: DocumentReference.ObserverOnError): () => void;
onSnapshot(observer: DocumentReference.Observer): () => void;
onSnapshot(documentListenOptions: DocumentReference.DocumentListenOptions, onNext: DocumentReference.ObserverOnNext, onError?: DocumentReference.ObserverOnError): () => void;
onSnapshot(documentListenOptions: DocumentReference.DocumentListenOptions, observer: DocumentReference.Observer): () => void;
set(data: object, writeOptions?: Types.WriteOptions): Promise<void>;
update(obj: object): Promise<void>;
update(key1: Types.UpdateKey, val1: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any, key5: Types.UpdateKey, val5: any): Promise<void>;
}
namespace DocumentReference {
interface DocumentListenOptions {
includeMetadataChanges: boolean;
}
type ObserverOnNext = (documentSnapshot: DocumentSnapshot) => void;
type ObserverOnError = (err: object) => void;
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
}
}
interface DocumentSnapshot {
readonly exists: boolean;
readonly id: string | null;
readonly metadata: Types.SnapshotMetadata;
readonly ref: DocumentReference;
data(): object | void;
get(fieldPath: string | FieldPath): any | undefined;
}
class FieldPath {
static documentId(): FieldPath;
constructor(...segments: string[]);
}
class FieldValue {
static delete(): FieldValue;
static serverTimestamp(): FieldValue;
}
class GeoPoint {
constructor(latitude: number, longitude: number);
readonly latitude: number;
readonly longitude: number;
}
class Path {
static fromName(name: string): Path;
constructor(pathComponents: string[]);
readonly id: string | null;
readonly isDocument: boolean;
readonly isCollection: boolean;
readonly relativeName: string;
child(relativePath: string): Path;
parent(): Path | null;
}
interface Query {
readonly firestore: Firestore;
endAt(snapshot: DocumentSnapshot): Query;
endAt(...varargs: any[]): Query;
endBefore(snapshot: DocumentSnapshot): Query;
endBefore(...varargs: any[]): Query;
get(): Promise<QuerySnapshot>;
limit(limit: number): Query;
onSnapshot(onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(observer: Query.Observer): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, observer: Query.Observer): () => void;
orderBy(fieldPath: string | FieldPath, directionStr?: Types.QueryDirection): Query;
startAfter(snapshot: DocumentSnapshot): Query;
startAfter(...varargs: any[]): Query;
startAt(snapshot: DocumentSnapshot): Query;
startAt(...varargs: any[]): Query;
where(fieldPath: string, op: Types.QueryOperator, value: any): Query;
}
namespace Query {
interface NativeFieldPath {
elements?: string[];
string?: string;
type: 'fieldpath' | 'string';
}
interface FieldFilter {
fieldPath: NativeFieldPath;
operator: string;
value: any;
}
interface FieldOrder {
direction: string;
fieldPath: NativeFieldPath;
}
interface QueryOptions {
endAt?: any[];
endBefore?: any[];
limit?: number;
offset?: number;
selectFields?: string[];
startAfter?: any[];
startAt?: any[];
}
// The JS code expects at least one of 'includeDocumentMetadataChanges'
// or 'includeQueryMetadataChanges' to be defined.
interface _IncludeDocumentMetadataChanges {
includeDocumentMetadataChanges: boolean;
}
interface _IncludeQueryMetadataChanges {
includeQueryMetadataChanges: boolean;
}
type QueryListenOptions = _IncludeDocumentMetadataChanges | _IncludeQueryMetadataChanges | (_IncludeDocumentMetadataChanges & _IncludeQueryMetadataChanges);
type ObserverOnNext = (querySnapshot: QuerySnapshot) => void;
type ObserverOnError = (err: object) => void;
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
}
}
interface QuerySnapshot {
readonly docChanges: DocumentChange[];
readonly docs: DocumentSnapshot[];
readonly empty: boolean;
readonly metadata: Types.SnapshotMetadata;
readonly query: Query;
readonly size: number;
forEach(callback: (snapshot: DocumentSnapshot) => any): void;
}
namespace QuerySnapshot {
interface NativeData {
changes: Types.NativeDocumentChange[];
documents: Types.NativeDocumentSnapshot[];
metadata: Types.SnapshotMetadata;
}
}
interface WriteBatch {
commit(): Promise<void>;
delete(docRef: DocumentReference): WriteBatch;
set(docRef: DocumentReference, data: object, options?: Types.WriteOptions): WriteBatch;
// multiple overrides for update() to allow strong-typed var_args
update(docRef: DocumentReference, obj: object): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any, key5: Types.UpdateKey, val5: any): WriteBatch;
}
namespace Types {
interface NativeDocumentChange {
document: NativeDocumentSnapshot;
newIndex: number;
oldIndex: number;
type: string;
}
interface NativeDocumentSnapshot {
data: {
[key: string]: TypeMap;
};
metadata: SnapshotMetadata;
path: string;
}
interface SnapshotMetadata {
fromCache: boolean;
hasPendingWrites: boolean;
}
type QueryDirection = 'asc' | 'ASC' | 'desc' | 'DESC';
type QueryOperator = '=' | '==' | '>' | '>=' | '<' | '<=';
interface TypeMap {
type: 'array' | 'boolean' | 'date' | 'documentid' | 'fieldvalue' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string';
value: any;
}
/** The key in update() function for DocumentReference and WriteBatch. */
type UpdateKey = string | FieldPath
interface WriteOptions {
merge?: boolean;
}
}
}
}
}

View File

@ -11,13 +11,15 @@ const adMobPropTypes = {
...ViewPropTypes,
size: PropTypes.string.isRequired,
unitId: PropTypes.string.isRequired,
/* eslint-disable react/forbid-prop-types */
request: PropTypes.object,
video: PropTypes.object,
/* eslint-enable react/forbid-prop-types */
};
Object.keys(EventTypes).forEach((eventType) => {
Object.keys(EventTypes).forEach(eventType => {
adMobPropTypes[eventType] = PropTypes.func;
});
Object.keys(NativeExpressEventTypes).forEach((eventType) => {
Object.keys(NativeExpressEventTypes).forEach(eventType => {
adMobPropTypes[eventType] = PropTypes.func;
});
@ -67,7 +69,8 @@ class AdMobComponent extends React.Component {
}
}
if (nativeEvent.type === 'onSizeChange') this.updateSize(nativeEvent.payload);
if (nativeEvent.type === 'onSizeChange')
this.updateSize(nativeEvent.payload);
};
/**

View File

@ -1,5 +1,4 @@
export default class AdRequest {
constructor() {
this._props = {
keywords: [],
@ -12,7 +11,7 @@ export default class AdRequest {
}
addTestDevice(deviceId?: string) {
this._props.testDevices.push(deviceId ? deviceId : 'DEVICE_ID_EMULATOR');
this._props.testDevices.push(deviceId || 'DEVICE_ID_EMULATOR');
return this;
}

View File

@ -2,12 +2,7 @@ import React from 'react';
import AdMobComponent from './AdMobComponent';
function Banner({ ...props }) {
return (
<AdMobComponent
{...props}
class={'RNFirebaseAdMobBanner'}
/>
);
return <AdMobComponent {...props} class="RNFirebaseAdMobBanner" />;
}
Banner.propTypes = AdMobComponent.propTypes;

View File

@ -1,4 +1,6 @@
/**
* @flow
*/
export default {
onAdLoaded: 'onAdLoaded',
onAdOpened: 'onAdOpened',
@ -18,4 +20,4 @@ export const NativeExpressEventTypes = {
export const RewardedVideoEventTypes = {
onRewarded: 'onRewarded',
onRewardedVideoStarted: 'onRewardedVideoStarted',
};
};

View File

@ -1,19 +1,20 @@
import { NativeModules, Platform } from 'react-native';
import { Platform } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
import type AdMob from './';
let subscriptions = [];
export default class Interstitial {
_admob: AdMob;
constructor(admob: Object, adUnit: string) {
constructor(admob: AdMob, adUnit: string) {
// Interstitials on iOS require a new instance each time
if (Platform.OS === 'ios') {
FirebaseAdMob.clearInterstitial(adUnit);
getNativeModule(admob).clearInterstitial(adUnit);
}
for (let i = 0, len = subscriptions.length; i < len; i++) {
@ -21,11 +22,14 @@ export default class Interstitial {
}
subscriptions = [];
this.admob = admob;
this._admob = admob;
this.adUnit = adUnit;
this.loaded = false;
SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
SharedEventEmitter.addListener(`interstitial_${adUnit}`, this._onInterstitialEvent);
SharedEventEmitter.addListener(
`interstitial_${adUnit}`,
this._onInterstitialEvent
);
}
/**
@ -33,7 +37,7 @@ export default class Interstitial {
* @param event
* @private
*/
_onInterstitialEvent = (event) => {
_onInterstitialEvent = event => {
const eventType = `interstitial:${this.adUnit}:${event.type}`;
let emitData = Object.assign({}, event);
@ -65,7 +69,10 @@ export default class Interstitial {
adRequest = new AdRequest().addTestDevice().build();
}
return FirebaseAdMob.interstitialLoadAd(this.adUnit, adRequest);
return getNativeModule(this._admob).interstitialLoadAd(
this.adUnit,
adRequest
);
}
/**
@ -82,7 +89,7 @@ export default class Interstitial {
*/
show() {
if (this.loaded) {
FirebaseAdMob.interstitialShowAd(this.adUnit);
getNativeModule(this._admob).interstitialShowAd(this.adUnit);
}
}
@ -94,11 +101,18 @@ export default class Interstitial {
*/
on(eventType, listenerCb) {
if (!statics.EventTypes[eventType]) {
console.warn(`Invalid event type provided, must be one of: ${Object.keys(statics.EventTypes).join(', ')}`);
console.warn(
`Invalid event type provided, must be one of: ${Object.keys(
statics.EventTypes
).join(', ')}`
);
return null;
}
const sub = SharedEventEmitter.addListener(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(
`interstitial:${this.adUnit}:${eventType}`,
listenerCb
);
subscriptions.push(sub);
return sub;
}

View File

@ -2,12 +2,7 @@ import React from 'react';
import AdMobComponent from './AdMobComponent';
function NativeExpress({ ...props }) {
return (
<AdMobComponent
{...props}
class={'RNFirebaseAdMobNativeExpress'}
/>
);
return <AdMobComponent {...props} class="RNFirebaseAdMobNativeExpress" />;
}
NativeExpress.propTypes = AdMobComponent.propTypes;

View File

@ -1,26 +1,29 @@
import { NativeModules } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
import type AdMob from './';
let subscriptions = [];
export default class RewardedVideo {
_admob: AdMob;
constructor(admob: Object, adUnit: string) {
constructor(admob: AdMob, adUnit: string) {
for (let i = 0, len = subscriptions.length; i < len; i++) {
subscriptions[i].remove();
}
subscriptions = [];
this.admob = admob;
this._admob = admob;
this.adUnit = adUnit;
this.loaded = false;
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
SharedEventEmitter.addListener(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
SharedEventEmitter.addListener(
`rewarded_video_${adUnit}`,
this._onRewardedVideoEvent
);
}
/**
@ -28,7 +31,7 @@ export default class RewardedVideo {
* @param event
* @private
*/
_onRewardedVideoEvent = (event) => {
_onRewardedVideoEvent = event => {
const eventType = `rewarded_video:${this.adUnit}:${event.type}`;
let emitData = Object.assign({}, event);
@ -60,7 +63,10 @@ export default class RewardedVideo {
adRequest = new AdRequest().addTestDevice().build();
}
return FirebaseAdMob.rewardedVideoLoadAd(this.adUnit, adRequest);
return getNativeModule(this._admob).rewardedVideoLoadAd(
this.adUnit,
adRequest
);
}
/**
@ -77,7 +83,7 @@ export default class RewardedVideo {
*/
show() {
if (this.loaded) {
FirebaseAdMob.rewardedVideoShowAd(this.adUnit);
getNativeModule(this._admob).rewardedVideoShowAd(this.adUnit);
}
}
@ -94,11 +100,18 @@ export default class RewardedVideo {
};
if (!types[eventType]) {
console.warn(`Invalid event type provided, must be one of: ${Object.keys(types).join(', ')}`);
console.warn(
`Invalid event type provided, must be one of: ${Object.keys(types).join(
', '
)}`
);
return null;
}
const sub = SharedEventEmitter.addListener(`rewarded_video:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(
`rewarded_video:${this.adUnit}:${eventType}`,
listenerCb
);
subscriptions.push(sub);
return sub;
}

View File

@ -1,5 +1,4 @@
export default class VideoOptions {
constructor() {
this._props = {
startMuted: true,

View File

@ -25,14 +25,11 @@ type NativeEvent = {
adUnit: string,
payload: Object,
type: string,
}
};
const NATIVE_EVENTS = [
'interstitial_event',
'rewarded_video_event',
];
const NATIVE_EVENTS = ['interstitial_event', 'rewarded_video_event'];
export const MODULE_NAME = 'RNFirebaseAdmob';
export const MODULE_NAME = 'RNFirebaseAdMob';
export const NAMESPACE = 'admob';
export default class AdMob extends ModuleBase {
@ -43,21 +40,28 @@ export default class AdMob extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
this._initialized = false;
this._appId = null;
SharedEventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
SharedEventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
SharedEventEmitter.addListener(
'interstitial_event',
this._onInterstitialEvent.bind(this)
);
SharedEventEmitter.addListener(
'rewarded_video_event',
this._onRewardedVideoEvent.bind(this)
);
}
_onInterstitialEvent(event: NativeEvent): void {
const { adUnit } = event;
const jsEventType = `interstitial_${adUnit}`;
if (!SharedEventEmitter.hasListeners(jsEventType)) {
if (SharedEventEmitter.listeners(jsEventType).length === 0) {
// TODO
}
@ -68,7 +72,7 @@ export default class AdMob extends ModuleBase {
const { adUnit } = event;
const jsEventType = `rewarded_video_${adUnit}`;
if (!SharedEventEmitter.hasListeners(jsEventType)) {
if (SharedEventEmitter.listeners(jsEventType).length === 0) {
// TODO
}
@ -87,7 +91,9 @@ export default class AdMob extends ModuleBase {
openDebugMenu(): void {
if (!this._initialized) {
getLogger(this).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 {
getLogger(this).info('Opening debug menu');
getNativeModule(this).openDebugMenu(this._appId);

View File

@ -32,6 +32,7 @@ export default class Analytics extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
@ -45,16 +46,21 @@ export default class Analytics extends ModuleBase {
logEvent(name: string, params: Object = {}): void {
// check name is not a reserved event name
if (ReservedEventNames.includes(name)) {
throw new Error(`event name '${name}' is a reserved event name and can not be used.`);
throw new Error(
`event name '${name}' is a reserved event name and can not be used.`
);
}
// name format validation
if (!AlphaNumericUnderscore.test(name)) {
throw new Error(`Event name '${name}' is invalid. Names should contain 1 to 32 alphanumeric characters or underscores.`);
throw new Error(
`Event name '${name}' is invalid. Names should contain 1 to 32 alphanumeric characters or underscores.`
);
}
// maximum number of allowed params check
if (params && Object.keys(params).length > 25) throw new Error('Maximum number of parameters exceeded (25).');
if (params && Object.keys(params).length > 25)
throw new Error('Maximum number of parameters exceeded (25).');
// Parameter names can be up to 24 characters long and must start with an alphabetic character
// and contain only alphanumeric characters and underscores. Only String, long and double param
@ -120,9 +126,9 @@ export default class Analytics extends ModuleBase {
* @param object
*/
setUserProperties(object: Object): void {
for (const property of Object.keys(object)) {
Object.keys(object).forEach(property => {
getNativeModule(this).setUserProperty(property, object[property]);
}
});
}
}

View File

@ -25,8 +25,10 @@ export default class ConfirmationResult {
* @param verificationCode
* @return {*}
*/
confirm(verificationCode: string): Promise<?User> {
return this._auth._interceptUserValue(getNativeModule(this._auth)._confirmVerificationCode(verificationCode));
confirm(verificationCode: string): Promise<User> {
return getNativeModule(this._auth)
._confirmVerificationCode(verificationCode)
.then(user => this._auth._setUser(user));
}
get verificationId(): string | null {

View File

@ -1,7 +1,14 @@
// @flow
import INTERNALS from '../../utils/internals';
import { SharedEventEmitter } from '../../utils/events';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from '../../utils';
import {
generatePushID,
isFunction,
isAndroid,
isIOS,
isString,
nativeToJSError,
} from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
@ -50,9 +57,15 @@ export default class PhoneAuthListener {
// internal events
this._internalEvents = {
codeSent: `phone:auth:${this._phoneAuthRequestKey}:onCodeSent`,
verificationFailed: `phone:auth:${this._phoneAuthRequestKey}:onVerificationFailed`,
verificationComplete: `phone:auth:${this._phoneAuthRequestKey}:onVerificationComplete`,
codeAutoRetrievalTimeout: `phone:auth:${this._phoneAuthRequestKey}:onCodeAutoRetrievalTimeout`,
verificationFailed: `phone:auth:${
this._phoneAuthRequestKey
}:onVerificationFailed`,
verificationComplete: `phone:auth:${
this._phoneAuthRequestKey
}:onVerificationComplete`,
codeAutoRetrievalTimeout: `phone:auth:${
this._phoneAuthRequestKey
}:onCodeAutoRetrievalTimeout`,
};
// user observer events
@ -73,14 +86,14 @@ export default class PhoneAuthListener {
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
this._timeout
);
}
if (isIOS) {
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._phoneAuthRequestKey
);
}
}
@ -94,8 +107,11 @@ export default class PhoneAuthListener {
for (let i = 0, len = events.length; i < len; i++) {
const type = events[i];
// $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));
SharedEventEmitter.once(
this._internalEvents[type],
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
this[`_${type}Handler`].bind(this)
);
}
}
@ -143,14 +159,15 @@ export default class PhoneAuthListener {
* @private
*/
_removeAllListeners() {
setTimeout(() => { // move to next event loop - not sure if needed
setTimeout(() => {
// move to next event loop - not sure if needed
// internal listeners
Object.values(this._internalEvents).forEach((event) => {
Object.values(this._internalEvents).forEach(event => {
SharedEventEmitter.removeAllListeners(event);
});
// user observer listeners
Object.values(this._publicEvents).forEach((publicEvent) => {
Object.values(this._publicEvents).forEach(publicEvent => {
SharedEventEmitter.removeAllListeners(publicEvent);
});
}, 0);
@ -163,12 +180,12 @@ export default class PhoneAuthListener {
_promiseDeferred() {
if (!this._promise) {
this._promise = new Promise((resolve, reject) => {
this._resolve = (result) => {
this._resolve = result => {
this._resolve = null;
return resolve(result);
};
this._reject = (possibleError) => {
this._reject = possibleError => {
this._reject = null;
return reject(possibleError);
};
@ -261,22 +278,36 @@ export default class PhoneAuthListener {
this._removeAllListeners();
}
/* -------------
-- PUBLIC API
--------------*/
on(event: string, observer: () => PhoneAuthSnapshot, errorCb?: () => PhoneAuthError, successCb?: () => PhoneAuthSnapshot): this {
on(
event: string,
observer: () => PhoneAuthSnapshot,
errorCb?: () => PhoneAuthError,
successCb?: () => PhoneAuthSnapshot
): this {
if (!isString(event)) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on'));
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on')
);
}
if (event !== 'state_changed') {
throw new Error(INTERNALS.STRINGS.ERROR_ARG_INVALID_VALUE('event', 'state_changed', event));
throw new Error(
INTERNALS.STRINGS.ERROR_ARG_INVALID_VALUE(
'event',
'state_changed',
event
)
);
}
if (!isFunction(observer)) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on'));
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on')
);
}
this._addUserObserver(observer);

View File

@ -6,19 +6,13 @@ 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,
}
import type {
ActionCodeSettings,
AuthCredential,
NativeUser,
UserCredential,
UserMetadata,
} from './types';
type UserInfo = {
displayName?: string,
@ -27,7 +21,12 @@ type UserInfo = {
photoURL?: string,
providerId: string,
uid: string,
}
};
type UpdateProfile = {
displayName?: string,
photoURL?: string,
};
export default class User {
_auth: Auth;
@ -63,6 +62,10 @@ export default class User {
return this._user.isAnonymous || false;
}
get metadata(): UserMetadata {
return this._user.metadata;
}
get phoneNumber(): ?string {
return this._user.phoneNumber || null;
}
@ -92,8 +95,11 @@ export default class User {
* @return {Promise}
*/
delete(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).delete());
return getNativeModule(this._auth)
.delete()
.then(() => {
this._auth._setUser();
});
}
/**
@ -105,12 +111,48 @@ export default class User {
}
/**
*
* 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);
}
/**
* @deprecated Deprecated linkWithCredential in favor of linkAndRetrieveDataWithCredential.
* @param credential
*/
linkWithCredential(credential: AuthCredential): Promise<User> {
return this._auth
._interceptUserValue(getNativeModule(this._auth).link(credential.providerId, credential.token, credential.secret));
console.warn(
'Deprecated firebase.User.prototype.linkWithCredential in favor of firebase.User.prototype.linkAndRetrieveDataWithCredential.'
);
return getNativeModule(this._auth)
.linkWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => this._auth._setUser(user));
}
/**
*
* @param credential
*/
linkAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this._auth)
.linkAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
@ -118,8 +160,34 @@ export default class User {
* @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));
console.warn(
'Deprecated firebase.User.prototype.reauthenticateWithCredential in favor of firebase.User.prototype.reauthenticateAndRetrieveDataWithCredential.'
);
return getNativeModule(this._auth)
.reauthenticateWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => {
this._auth._setUser(user);
});
}
/**
* Re-authenticate a user with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
reauthenticateAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this._auth)
.reauthenticateAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
@ -127,16 +195,24 @@ export default class User {
* @return {Promise}
*/
reload(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).reload());
return getNativeModule(this._auth)
.reload()
.then(user => {
this._auth._setUser(user);
});
}
/**
* Send verification email to current user.
*/
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).sendEmailVerification(actionCodeSettings));
sendEmailVerification(
actionCodeSettings?: ActionCodeSettings
): Promise<void> {
return getNativeModule(this._auth)
.sendEmailVerification(actionCodeSettings)
.then(user => {
this._auth._setUser(user);
});
}
toJSON(): Object {
@ -149,7 +225,9 @@ export default class User {
* @return {Promise.<TResult>|*}
*/
unlink(providerId: string): Promise<User> {
return this._auth._interceptUserValue(getNativeModule(this._auth).unlink(providerId));
return getNativeModule(this._auth)
.unlink(providerId)
.then(user => this._auth._setUser(user));
}
/**
@ -159,8 +237,11 @@ export default class User {
* @return {Promise} A promise resolved upon completion
*/
updateEmail(email: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).updateEmail(email));
return getNativeModule(this._auth)
.updateEmail(email)
.then(user => {
this._auth._setUser(user);
});
}
/**
@ -169,8 +250,11 @@ export default class User {
* @return {Promise}
*/
updatePassword(password: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(getNativeModule(this._auth).updatePassword(password));
return getNativeModule(this._auth)
.updatePassword(password)
.then(user => {
this._auth._setUser(user);
});
}
/**
@ -178,58 +262,81 @@ export default class User {
* @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);
updateProfile(updates: UpdateProfile = {}): Promise<void> {
return getNativeModule(this._auth)
.updateProfile(updates)
.then(user => {
this._auth._setUser(user);
});
}
/**
* 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'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithPhoneNumber'
)
);
}
linkWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup')
);
}
linkWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithRedirect'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithRedirect'
)
);
}
reauthenticateWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPhoneNumber'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPhoneNumber'
)
);
}
reauthenticateWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPopup'
)
);
}
reauthenticateWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithRedirect'
)
);
}
updatePhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', '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'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken')
);
}
}

View File

@ -15,21 +15,36 @@ import EmailAuthProvider from './providers/EmailAuthProvider';
import PhoneAuthProvider from './providers/PhoneAuthProvider';
import GoogleAuthProvider from './providers/GoogleAuthProvider';
import GithubAuthProvider from './providers/GithubAuthProvider';
import OAuthProvider from './providers/OAuthProvider';
import TwitterAuthProvider from './providers/TwitterAuthProvider';
import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener';
import type { ActionCodeSettings, AuthCredential } from '../../types';
import type {
ActionCodeSettings,
AuthCredential,
NativeUser,
NativeUserCredential,
UserCredential,
} from './types';
import type App from '../core/firebase-app';
type AuthResult = {
authenticated: boolean,
user: Object|null
} | null;
type AuthState = {
user?: NativeUser,
};
type ActionCodeInfo = {
data: {
email?: string,
fromEmail?: string,
},
operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL',
};
const NATIVE_EVENTS = [
'auth_state_changed',
'auth_id_token_changed',
'phone_auth_state_changed',
];
@ -37,99 +52,79 @@ export const MODULE_NAME = 'RNFirebaseAuth';
export const NAMESPACE = 'auth';
export default class Auth extends ModuleBase {
_authResult: AuthResult | null;
_authResult: boolean;
_languageCode: string;
_user: User | null;
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._user = null;
this._authResult = null;
this._authResult = false;
this._languageCode =
getNativeModule(this).APP_LANGUAGE[app._name] ||
getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onAuthStateChanged
getAppEventName(this, 'auth_state_changed'),
this._onInternalAuthStateChanged.bind(this),
(state: AuthState) => {
this._setUser(state.user);
SharedEventEmitter.emit(
getAppEventName(this, 'onAuthStateChanged'),
this._user
);
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public events based on event.type
getAppEventName(this, 'phone_auth_state_changed'),
this._onInternalPhoneAuthStateChanged.bind(this),
(event: Object) => {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
SharedEventEmitter.emit(eventKey, event.state);
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onIdTokenChanged
getAppEventName(this, 'auth_id_token_changed'),
this._onInternalIdTokenChanged.bind(this),
(auth: AuthState) => {
this._setUser(auth.user);
SharedEventEmitter.emit(
getAppEventName(this, 'onIdTokenChanged'),
this._user
);
}
);
getNativeModule(this).addAuthStateListener();
getNativeModule(this).addIdTokenListener();
}
/**
* Route a phone state change event to the correct listeners
* @param event
* @private
*/
_onInternalPhoneAuthStateChanged(event: Object) {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
SharedEventEmitter.emit(eventKey, event.state);
}
_setAuthState(auth: AuthResult) {
this._authResult = auth;
this._user = auth && auth.user ? new User(this, auth.user) : null;
_setUser(user: ?NativeUser): ?User {
this._authResult = true;
this._user = user ? new User(this, user) : null;
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
return this._user;
}
/**
* Internal auth changed listener
* @param auth
* @private
*/
_onInternalAuthStateChanged(auth: AuthResult) {
this._setAuthState(auth);
SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user);
}
/**
* Internal auth changed listener
* @param auth
* @param emit
* @private
*/
_onInternalIdTokenChanged(auth: AuthResult) {
this._setAuthState(auth);
SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user);
}
/**
* Intercept all user actions and send their results to
* auth state change before resolving
* @param promise
* @returns {Promise.<*>}
* @private
*/
_interceptUserValue(promise: Promise<AuthResult>): Promise<User> {
return promise.then((result: AuthResult) => {
if (!result) this._setAuthState(null);
else if (result.user) this._setAuthState(result);
else if (result.uid) this._setAuthState({ authenticated: true, user: result });
return this._user;
});
}
_interceptUndefinedUserValue(promise: Promise<AuthResult>): Promise<void> {
return this._interceptUserValue(promise)
.then(() => {});
_setUserCredential(userCredential: NativeUserCredential): UserCredential {
const user = new User(this, userCredential.user);
this._authResult = true;
this._user = user;
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
return {
additionalUserInfo: userCredential.additionalUserInfo,
user,
};
}
/*
@ -142,18 +137,19 @@ export default class Auth extends ModuleBase {
*/
onAuthStateChanged(listener: Function) {
getLogger(this).info('Creating onAuthStateChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onAuthStateChanged'), listener);
SharedEventEmitter.addListener(
getAppEventName(this, 'onAuthStateChanged'),
listener
);
if (this._authResult) listener(this._user || null);
return this._offAuthStateChanged.bind(this, listener);
}
/**
* Remove auth change listener
* @param listener
*/
_offAuthStateChanged(listener: Function) {
getLogger(this).info('Removing onAuthStateChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener);
return () => {
getLogger(this).info('Removing onAuthStateChanged listener');
SharedEventEmitter.removeListener(
getAppEventName(this, 'onAuthStateChanged'),
listener
);
};
}
/**
@ -162,18 +158,19 @@ export default class Auth extends ModuleBase {
*/
onIdTokenChanged(listener: Function) {
getLogger(this).info('Creating onIdTokenChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onIdTokenChanged'), listener);
SharedEventEmitter.addListener(
getAppEventName(this, 'onIdTokenChanged'),
listener
);
if (this._authResult) listener(this._user || null);
return this._offIdTokenChanged.bind(this, listener);
}
/**
* Remove id token change listener
* @param listener
*/
_offIdTokenChanged(listener: Function) {
getLogger(this).info('Removing onIdTokenChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener);
return () => {
getLogger(this).info('Removing onIdTokenChanged listener');
SharedEventEmitter.removeListener(
getAppEventName(this, 'onIdTokenChanged'),
listener
);
};
}
/**
@ -182,18 +179,19 @@ export default class Auth extends ModuleBase {
*/
onUserChanged(listener: Function) {
getLogger(this).info('Creating onUserChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onUserChanged'), listener);
SharedEventEmitter.addListener(
getAppEventName(this, 'onUserChanged'),
listener
);
if (this._authResult) listener(this._user || null);
return this._offUserChanged.bind(this, listener);
}
/**
* Remove user change listener
* @param listener
*/
_offUserChanged(listener: Function) {
getLogger(this).info('Removing onUserChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener);
return () => {
getLogger(this).info('Removing onUserChanged listener');
SharedEventEmitter.removeListener(
getAppEventName(this, 'onUserChanged'),
listener
);
};
}
/**
@ -201,15 +199,54 @@ export default class Auth extends ModuleBase {
* @return {Promise}
*/
signOut(): Promise<void> {
return this._interceptUndefinedUserValue(getNativeModule(this).signOut());
return getNativeModule(this)
.signOut()
.then(() => {
this._setUser();
});
}
/**
* Sign a user in anonymously
* @deprecated Deprecated signInAnonymously in favor of signInAnonymouslyAndRetrieveData.
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously(): Promise<User> {
console.warn(
'Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.'
);
return getNativeModule(this)
.signInAnonymously()
.then(user => this._setUser(user));
}
/**
* Sign a user in anonymously
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously(): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInAnonymously());
signInAnonymouslyAndRetrieveData(): Promise<UserCredential> {
return getNativeModule(this)
.signInAnonymouslyAndRetrieveData()
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Create a user with the email/password functionality
* @deprecated Deprecated createUserWithEmailAndPassword in favor of createUserAndRetrieveDataWithEmailAndPassword.
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
*/
createUserWithEmailAndPassword(
email: string,
password: string
): Promise<User> {
console.warn(
'Deprecated firebase.User.prototype.createUserWithEmailAndPassword in favor of firebase.User.prototype.createUserAndRetrieveDataWithEmailAndPassword.'
);
return getNativeModule(this)
.createUserWithEmailAndPassword(email, password)
.then(user => this._setUser(user));
}
/**
@ -218,8 +255,29 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
*/
createUserWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).createUserWithEmailAndPassword(email, password));
createUserAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
): Promise<User> {
return getNativeModule(this)
.createUserAndRetrieveDataWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in with email/password
* @deprecated Deprecated signInWithEmailAndPassword in favor of signInAndRetrieveDataWithEmailAndPassword
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
*/
signInWithEmailAndPassword(email: string, password: string): Promise<User> {
console.warn(
'Deprecated firebase.User.prototype.signInWithEmailAndPassword in favor of firebase.User.prototype.signInAndRetrieveDataWithEmailAndPassword.'
);
return getNativeModule(this)
.signInWithEmailAndPassword(email, password)
.then(user => this._setUser(user));
}
/**
@ -228,8 +286,28 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
*/
signInWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInWithEmailAndPassword(email, password));
signInAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a custom auth token
* @deprecated Deprecated signInWithCustomToken in favor of signInAndRetrieveDataWithCustomToken
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInWithCustomToken(customToken: string): Promise<User> {
console.warn(
'Deprecated firebase.User.prototype.signInWithCustomToken in favor of firebase.User.prototype.signInAndRetrieveDataWithCustomToken.'
);
return getNativeModule(this)
.signInWithCustomToken(customToken)
.then(user => this._setUser(user));
}
/**
@ -237,20 +315,46 @@ export default class Auth extends ModuleBase {
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInWithCustomToken(customToken: string): Promise<User> {
return this._interceptUserValue(getNativeModule(this).signInWithCustomToken(customToken));
signInAndRetrieveDataWithCustomToken(
customToken: string
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithCustomToken(customToken)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a third-party authentication provider
* @deprecated Deprecated signInWithCredential in favor of signInAndRetrieveDataWithCredential.
* @return {Promise} A promise resolved upon completion
*/
signInWithCredential(credential: AuthCredential): Promise<User> {
console.warn(
'Deprecated firebase.User.prototype.signInWithCredential in favor of firebase.User.prototype.signInAndRetrieveDataWithCredential.'
);
return getNativeModule(this)
.signInWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => this._setUser(user));
}
/**
* Sign the user in with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
signInWithCredential(credential: AuthCredential): Promise<User> {
return this._interceptUserValue(
getNativeModule(this).signInWithCredential(
credential.providerId, credential.token, credential.secret,
),
);
signInAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
@ -258,9 +362,9 @@ export default class Auth extends ModuleBase {
*
*/
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return getNativeModule(this).signInWithPhoneNumber(phoneNumber).then((result) => {
return new ConfirmationResult(this, result.verificationId);
});
return getNativeModule(this)
.signInWithPhoneNumber(phoneNumber)
.then(result => new ConfirmationResult(this, result.verificationId));
}
/**
@ -272,7 +376,10 @@ export default class Auth extends ModuleBase {
* @param autoVerifyTimeout Android Only
* @returns {PhoneAuthListener}
*/
verifyPhoneNumber(phoneNumber: string, autoVerifyTimeout?: number): PhoneAuthListener {
verifyPhoneNumber(
phoneNumber: string,
autoVerifyTimeout?: number
): PhoneAuthListener {
return new PhoneAuthListener(this, phoneNumber, autoVerifyTimeout);
}
@ -280,8 +387,14 @@ export default class Auth extends ModuleBase {
* Send reset password instructions via email
* @param {string} email The email to send password reset instructions
*/
sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void> {
return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings);
sendPasswordResetEmail(
email: string,
actionCodeSettings?: ActionCodeSettings
): Promise<void> {
return getNativeModule(this).sendPasswordResetEmail(
email,
actionCodeSettings
);
}
/**
@ -314,26 +427,32 @@ export default class Auth extends ModuleBase {
* @param code
* @return {Promise.<any>|Promise<ActionCodeInfo>}
*/
checkActionCode(code: string): Promise<void> {
checkActionCode(code: string): Promise<ActionCodeInfo> {
return getNativeModule(this).checkActionCode(code);
}
/**
* Get the currently signed in user
* @return {Promise}
*/
getCurrentUser(): Promise<User | null> {
return this._interceptUserValue(getNativeModule(this).getCurrentUser());
}
/**
* Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
* @return {Promise}
*/
fetchProvidersForEmail(email: string): Promise<Array<String>> {
fetchProvidersForEmail(email: string): Promise<string[]> {
return getNativeModule(this).fetchProvidersForEmail(email);
}
verifyPasswordResetCode(code: string): Promise<string> {
return getNativeModule(this).verifyPasswordResetCode(code);
}
/**
* Sets the language for the auth module
* @param code
* @returns {*}
*/
set languageCode(code: string) {
this._languageCode = code;
getNativeModule(this).setLanguageCode(code);
}
/**
* Get the currently signed in user
* @return {Promise}
@ -342,28 +461,58 @@ export default class Auth extends ModuleBase {
return this._user;
}
get languageCode(): string {
return this._languageCode;
}
/**
* KNOWN UNSUPPORTED METHODS
*/
getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'getRedirectResult'
)
);
}
setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence'));
}
signInAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInAndRetrieveDataWithCredential'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'setPersistence'
)
);
}
signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'signInWithPopup'
)
);
}
signInWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithRedirect'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'signInWithRedirect'
)
);
}
// firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680
useDeviceLanguage() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'useDeviceLanguage'
)
);
}
}
@ -374,6 +523,7 @@ export const statics = {
GithubAuthProvider,
TwitterAuthProvider,
FacebookAuthProvider,
OAuthProvider,
PhoneAuthState: {
CODE_SENT: 'sent',
AUTO_VERIFY_TIMEOUT: 'timeout',

View File

@ -2,13 +2,15 @@
* @flow
* EmailAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'password';
export default class EmailAuthProvider {
constructor() {
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(): string {

View File

@ -2,13 +2,15 @@
* @flow
* FacebookAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'facebook.com';
export default class FacebookAuthProvider {
constructor() {
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(): string {

View File

@ -2,13 +2,15 @@
* @flow
* GithubAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'github.com';
export default class GithubAuthProvider {
constructor() {
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(): string {

View File

@ -2,13 +2,15 @@
* @flow
* EmailAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'google.com';
export default class GoogleAuthProvider {
constructor() {
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(): string {

View File

@ -0,0 +1,27 @@
/**
* @flow
* OAuthProvider representation wrapper
*/
import type { AuthCredential } from '../types';
const providerId = 'oauth';
export default class OAuthProvider {
constructor() {
throw new Error(
'`new OAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {
return providerId;
}
static credential(idToken: string, accessToken: string): AuthCredential {
return {
token: idToken,
secret: accessToken,
providerId,
};
}
}

View File

@ -2,13 +2,15 @@
* @flow
* PhoneAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'phone';
export default class PhoneAuthProvider {
constructor() {
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(): string {

View File

@ -2,13 +2,15 @@
* @flow
* TwitterAuthProvider representation wrapper
*/
import type { AuthCredential } from '../../../types';
import type { AuthCredential } from '../types';
const providerId = 'twitter.com';
export default class TwitterAuthProvider {
constructor() {
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(): string {

67
lib/modules/auth/types.js Normal file
View File

@ -0,0 +1,67 @@
/**
* @flow
*/
import type User from './User';
export type ActionCodeSettings = {
android: {
installApp?: boolean,
minimumVersion?: string,
packageName: string,
},
handleCodeInApp?: boolean,
iOS: {
bundleId?: string,
},
url: string,
};
type AdditionalUserInfo = {
isNewUser: boolean,
profile?: Object,
providerId: string,
username?: string,
};
export type AuthCredential = {
providerId: string,
token: string,
secret: string,
};
export type UserCredential = {|
additionalUserInfo?: AdditionalUserInfo,
user: User,
|};
export type UserInfo = {
displayName?: string,
email?: string,
phoneNumber?: string,
photoURL?: string,
providerId: string,
uid: string,
};
export type UserMetadata = {
creationTime?: string,
lastSignInTime?: string,
};
export type NativeUser = {
displayName?: string,
email?: string,
emailVerified?: boolean,
isAnonymous?: boolean,
metadata: UserMetadata,
phoneNumber?: string,
photoURL?: string,
providerData: UserInfo[],
providerId: string,
uid: string,
};
export type NativeUserCredential = {|
additionalUserInfo?: AdditionalUserInfo,
user: NativeUser,
|};

View File

@ -13,8 +13,11 @@ type NativeValue = {
numberValue?: number,
dataValue?: Object,
boolValue?: boolean,
source: 'remoteConfigSourceRemote' | 'remoteConfigSourceDefault' | ' remoteConfigSourceStatic',
}
source:
| 'remoteConfigSourceRemote'
| 'remoteConfigSourceDefault'
| ' remoteConfigSourceStatic',
};
export const MODULE_NAME = 'RNFirebaseRemoteConfig';
export const NAMESPACE = 'config';
@ -28,6 +31,7 @@ export default class RemoteConfig extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;
@ -43,9 +47,26 @@ export default class RemoteConfig extends ModuleBase {
return {
source: nativeValue.source,
val() {
if (nativeValue.boolValue !== null && (nativeValue.stringValue === 'true' || nativeValue.stringValue === 'false' || nativeValue.stringValue === null)) return nativeValue.boolValue;
if (nativeValue.numberValue !== null && nativeValue.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.boolValue !== null &&
(nativeValue.stringValue === 'true' ||
nativeValue.stringValue === 'false' ||
nativeValue.stringValue === null)
)
return nativeValue.boolValue;
if (
nativeValue.numberValue !== null &&
nativeValue.numberValue !== undefined &&
(nativeValue.stringValue == null ||
nativeValue.stringValue === '' ||
nativeValue.numberValue.toString() === nativeValue.stringValue)
)
return nativeValue.numberValue;
if (
nativeValue.dataValue !== nativeValue.stringValue &&
(nativeValue.stringValue == null || nativeValue.stringValue === '')
)
return nativeValue.dataValue;
return nativeValue.stringValue;
},
};
@ -69,7 +90,9 @@ export default class RemoteConfig extends ModuleBase {
*/
fetch(expiration?: number) {
if (expiration !== undefined) {
getLogger(this).debug(`Fetching remote config data with expiration ${expiration.toString()}`);
getLogger(this).debug(
`Fetching remote config data with expiration ${expiration.toString()}`
);
return getNativeModule(this).fetchWithExpirationDuration(expiration);
}
getLogger(this).debug('Fetching remote config data');
@ -123,7 +146,7 @@ export default class RemoteConfig extends ModuleBase {
getValues(keys: Array<String>) {
return getNativeModule(this)
.getValues(keys || [])
.then((nativeValues) => {
.then(nativeValues => {
const values: { [String]: Object } = {};
for (let i = 0, len = keys.length; i < len; i++) {
values[keys[i]] = this._nativeValueToJS(nativeValues[i]);

View File

@ -13,7 +13,9 @@ 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 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';
@ -22,15 +24,12 @@ 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';
import type { FirebaseOptions } from '../../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class App {
_extendedProps: { [string] : boolean };
_extendedProps: { [string]: boolean };
_initialized: boolean = false;
_name: string;
_nativeInitialized: boolean = false;
@ -51,7 +50,11 @@ export default class App {
storage: () => Storage;
utils: () => Utils;
constructor(name: string, options: FirebaseOptions, fromNative: boolean = false) {
constructor(
name: string,
options: FirebaseOptions,
fromNative: boolean = false
) {
this._name = name;
this._options = Object.assign({}, options);
@ -59,10 +62,14 @@ export default class App {
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 });
});
FirebaseCoreModule.initializeApp(
this._name,
this._options,
(error, result) => {
this._initialized = true;
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
}
);
}
// modules
@ -109,7 +116,10 @@ export default class App {
* @param props
*/
extendApp(props: Object) {
if (!isObject(props)) throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG('Object', 'extendApp'));
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++) {
@ -130,7 +140,9 @@ export default class App {
* @return {Promise}
*/
delete() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', '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(
@ -141,7 +153,6 @@ export default class App {
// return FirebaseCoreModule.deleteApp(this._name);
}
/**
*
* @return {*}

View File

@ -7,21 +7,58 @@ 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 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 {
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,
@ -59,25 +96,57 @@ class Firebase {
constructor() {
if (!FirebaseCoreModule) {
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
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.analytics = APPS.moduleAndStatics(
'analytics',
AnalyticsStatics,
AnalyticsModuleName
);
this.auth = APPS.moduleAndStatics('auth', AuthStatics, AuthModuleName);
this.config = APPS.moduleAndStatics('config', ConfigStatics, ConfigModuleName);
this.config = APPS.moduleAndStatics(
'config',
ConfigStatics,
ConfigModuleName
);
this.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
this.database = APPS.moduleAndStatics('database', DatabaseStatics, DatabaseModuleName);
this.database = APPS.moduleAndStatics(
'database',
DatabaseStatics,
DatabaseModuleName
);
this.fabric = {
crashlytics: APPS.moduleAndStatics('crashlytics', CrashlyticsStatics, CrashlyticsModuleName),
crashlytics: APPS.moduleAndStatics(
'crashlytics',
CrashlyticsStatics,
CrashlyticsModuleName
),
};
this.firestore = APPS.moduleAndStatics('firestore', FirestoreStatics, FirestoreModuleName);
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.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);
}
@ -112,6 +181,14 @@ class Firebase {
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

@ -15,6 +15,7 @@ export default class Crash extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}

View File

@ -7,7 +7,6 @@ import { getNativeModule } from '../../utils/native';
import type Database from './';
import type Reference from './reference';
/**
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect
* @class Disconnect
@ -33,7 +32,10 @@ export default class Disconnect {
* @returns {*}
*/
set(value: string | Object): Promise<void> {
return getNativeModule(this._database).onDisconnectSet(this.path, { type: typeOf(value), value });
return getNativeModule(this._database).onDisconnectSet(this.path, {
type: typeOf(value),
value,
});
}
/**
@ -42,7 +44,10 @@ export default class Disconnect {
* @returns {*}
*/
update(values: Object): Promise<void> {
return getNativeModule(this._database).onDisconnectUpdate(this.path, values);
return getNativeModule(this._database).onDisconnectUpdate(
this.path,
values
);
}
/**

View File

@ -31,6 +31,7 @@ export default class Database extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._transactionHandler = new TransactionHandler(this);
@ -46,7 +47,7 @@ export default class Database extends ModuleBase {
setTimeout(() => {
this._serverTimeOffset = 0;
this._offsetRef = this.ref('.info/serverTimeOffset');
this._offsetRef.on('value', (snapshot) => {
this._offsetRef.on('value', snapshot => {
this._serverTimeOffset = snapshot.val() || this._serverTimeOffset;
});
}, 1);
@ -85,9 +86,13 @@ export default class Database extends ModuleBase {
}
export const statics = {
ServerValue: NativeModules.RNFirebaseDatabase ? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
} : {},
ServerValue: NativeModules.RNFirebaseDatabase
? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || {
'.sv': 'timestamp',
},
}
: {},
enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);

View File

@ -5,7 +5,7 @@
import { objectToUniqueId } from '../../utils';
import type { DatabaseModifier } from '../../types';
import type Reference from './reference.js';
import type Reference from './reference';
// todo doc methods
@ -16,7 +16,7 @@ export default class Query {
_reference: Reference;
modifiers: Array<DatabaseModifier>;
constructor(ref: Reference, path: string, existingModifiers?: Array<DatabaseModifier>) {
constructor(ref: Reference, existingModifiers?: Array<DatabaseModifier>) {
this.modifiers = existingModifiers ? [...existingModifiers] : [];
this._reference = ref;
}

View File

@ -2,7 +2,7 @@
* @flow
* Database Reference representation wrapper
*/
import Query from './query.js';
import Query from './query';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import { getLogger } from '../../utils/log';
@ -41,11 +41,11 @@ const ReferenceEventTypes = {
};
type DatabaseListener = {
listenerId: number;
eventName: string;
successCallback: Function;
failureCallback?: Function;
}
listenerId: number,
eventName: string,
successCallback: Function,
failureCallback?: Function,
};
/**
* @typedef {String} ReferenceLocation - Path to location in the database, relative
@ -80,12 +80,16 @@ export default class Reference extends ReferenceBase {
_query: Query;
_refListeners: { [listenerId: number]: DatabaseListener };
constructor(database: Database, path: string, existingModifiers?: Array<DatabaseModifier>) {
constructor(
database: Database,
path: string,
existingModifiers?: Array<DatabaseModifier>
) {
super(path);
this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, path, existingModifiers);
this._query = new Query(this, existingModifiers);
getLogger(database).debug('Created new Reference', this._getRefKey());
}
@ -100,7 +104,12 @@ export default class Reference extends ReferenceBase {
* @returns {*}
*/
keepSynced(bool: boolean): Promise<void> {
return getNativeModule(this._database).keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
return getNativeModule(this._database).keepSynced(
this._getRefKey(),
this.path,
this._query.getModifiers(),
bool
);
}
/**
@ -113,8 +122,11 @@ export default class Reference extends ReferenceBase {
*/
set(value: any, onComplete?: Function): Promise<void> {
return promiseOrCallback(
getNativeModule(this._database).set(this.path, this._serializeAnyType(value)),
onComplete,
getNativeModule(this._database).set(
this.path,
this._serializeAnyType(value)
),
onComplete
);
}
@ -126,12 +138,15 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setPriority(priority: string | number | null, onComplete?: Function): Promise<void> {
setPriority(
priority: string | number | null,
onComplete?: Function
): Promise<void> {
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
getNativeModule(this._database).setPriority(this.path, _priority),
onComplete,
onComplete
);
}
@ -144,13 +159,21 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setWithPriority(value: any, priority: string | number | null, onComplete?: Function): Promise<void> {
setWithPriority(
value: any,
priority: string | number | null,
onComplete?: Function
): Promise<void> {
const _value = this._serializeAnyType(value);
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
getNativeModule(this._database).setWithPriority(this.path, _value, _priority),
onComplete,
getNativeModule(this._database).setWithPriority(
this.path,
_value,
_priority
),
onComplete
);
}
@ -167,7 +190,7 @@ export default class Reference extends ReferenceBase {
return promiseOrCallback(
getNativeModule(this._database).update(this.path, value),
onComplete,
onComplete
);
}
@ -181,7 +204,7 @@ export default class Reference extends ReferenceBase {
remove(onComplete?: Function): Promise<void> {
return promiseOrCallback(
getNativeModule(this._database).remove(this.path),
onComplete,
onComplete
);
}
@ -196,10 +219,12 @@ export default class Reference extends ReferenceBase {
transaction(
transactionUpdate: Function,
onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *,
applyLocally: boolean = false,
applyLocally: boolean = false
) {
if (!isFunction(transactionUpdate)) {
return Promise.reject(new Error('Missing transactionUpdate function argument.'));
return Promise.reject(
new Error('Missing transactionUpdate function argument.')
);
}
return new Promise((resolve, reject) => {
@ -213,15 +238,22 @@ export default class Reference extends ReferenceBase {
}
if (error) return reject(error);
return resolve({ committed, snapshot: new Snapshot(this, snapshotData) });
return resolve({
committed,
snapshot: new Snapshot(this, snapshotData),
});
};
// start the transaction natively
this._database._transactionHandler.add(this, transactionUpdate, onCompleteWrapper, applyLocally);
this._database._transactionHandler.add(
this,
transactionUpdate,
onCompleteWrapper,
applyLocally
);
});
}
/**
*
* @param eventName
@ -234,21 +266,24 @@ export default class Reference extends ReferenceBase {
eventName: string = 'value',
successCallback: (snapshot: Object) => void,
cancelOrContext: (error: FirebaseError) => void,
context?: Object,
context?: Object
) {
return getNativeModule(this._database).once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
return getNativeModule(this._database)
.once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
.then(({ snapshot }) => {
const _snapshot = new Snapshot(this, snapshot);
if (isFunction(successCallback)) {
if (isObject(cancelOrContext)) successCallback.bind(cancelOrContext)(_snapshot);
if (context && isObject(context)) successCallback.bind(context)(_snapshot);
if (isObject(cancelOrContext))
successCallback.bind(cancelOrContext)(_snapshot);
if (context && isObject(context))
successCallback.bind(context)(_snapshot);
successCallback(_snapshot);
}
return _snapshot;
})
.catch((error) => {
.catch(error => {
if (isFunction(cancelOrContext)) return cancelOrContext(error);
return error;
});
@ -262,19 +297,27 @@ export default class Reference extends ReferenceBase {
*/
push(value: any, onComplete?: Function): Reference | Promise<void> {
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)}`
);
}
const newRef = new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
const newRef = new Reference(
this._database,
`${this.path}/${generatePushID(this._database._serverTimeOffset)}`
);
const promise = newRef.set(value);
// if callback provided then internally call the set promise with value
if (isFunction(onComplete)) {
return promise
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.catch(error => onComplete(error, null));
return (
promise
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.catch(error => onComplete(error, null))
);
}
// otherwise attach promise to 'thenable' reference and return the
@ -327,7 +370,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
orderBy(name: string, key?: string): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.orderBy(name, key);
return newRef;
}
@ -361,7 +408,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
limit(name: string, limit: number): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.limit(name, limit);
return newRef;
}
@ -408,7 +459,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
filter(name: string, value: any, key?: string): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.filter(name, value, key);
return newRef;
}
@ -438,7 +493,7 @@ export default class Reference extends ReferenceBase {
* @returns {string}
*/
toString(): string {
return this.path;
return `${this._database.app.options.databaseURL}/${this.path}`;
}
/**
@ -450,10 +505,12 @@ export default class Reference extends ReferenceBase {
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual}
*/
isEqual(otherRef: Reference): boolean {
return !!otherRef
&& otherRef.constructor === Reference
&& otherRef.key === this.key
&& this._query.queryIdentifier() === otherRef._query.queryIdentifier();
return (
!!otherRef &&
otherRef.constructor === Reference &&
otherRef.key === this.key &&
this._query.queryIdentifier() === otherRef._query.queryIdentifier()
);
}
/**
@ -468,7 +525,10 @@ export default class Reference extends ReferenceBase {
*/
get parent(): Reference | null {
if (this.path === '/') return null;
return new Reference(this._database, this.path.substring(0, this.path.lastIndexOf('/')));
return new Reference(
this._database,
this.path.substring(0, this.path.lastIndexOf('/'))
);
}
/**
@ -495,20 +555,23 @@ export default class Reference extends ReferenceBase {
* Access then method of promise if set
* @return {*}
*/
then(fnResolve: (any) => any, fnReject: (any) => any) {
then(fnResolve: any => any, fnReject: any => any) {
if (isFunction(fnResolve) && this._promise && this._promise.then) {
return this._promise.then.bind(this._promise)((result) => {
this._promise = null;
return fnResolve(result);
}, (possibleErr) => {
this._promise = null;
return this._promise.then.bind(this._promise)(
result => {
this._promise = null;
return fnResolve(result);
},
possibleErr => {
this._promise = null;
if (isFunction(fnReject)) {
return fnReject(possibleErr);
if (isFunction(fnReject)) {
return fnReject(possibleErr);
}
throw possibleErr;
}
throw possibleErr;
});
);
}
throw new Error("Cannot read property 'then' of undefined.");
@ -518,9 +581,9 @@ export default class Reference extends ReferenceBase {
* Access catch method of promise if set
* @return {*}
*/
catch(fnReject: (any) => any) {
catch(fnReject: any => any) {
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;
return fnReject(possibleErr);
});
@ -539,7 +602,9 @@ export default class Reference extends ReferenceBase {
* @return {string}
*/
_getRegistrationKey(eventType: string): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
return `$${this._database.app.name}$/${
this.path
}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
}
/**
@ -550,7 +615,9 @@ export default class Reference extends ReferenceBase {
* @private
*/
_getRefKey(): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}`;
return `$${this._database.app.name}$/${
this.path
}$${this._query.queryIdentifier()}`;
}
/**
@ -562,7 +629,6 @@ export default class Reference extends ReferenceBase {
this._promise = promise;
}
/**
*
* @param obj
@ -623,34 +689,65 @@ export default class Reference extends ReferenceBase {
*
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on}
*/
on(eventType: string, callback: (Snapshot) => any, cancelCallbackOrContext?: (Object) => any | Object, context?: Object): Function {
on(
eventType: string,
callback: Snapshot => any,
cancelCallbackOrContext?: Object => any | Object,
context?: Object
): Function {
if (!eventType) {
throw new Error('Query.on failed: Function called with 0 arguments. Expects at least 2.');
throw new Error(
'Query.on failed: Function called with 0 arguments. Expects at least 2.'
);
}
if (!isString(eventType) || !ReferenceEventTypes[eventType]) {
throw new Error(`Query.on failed: First argument must be a valid string event type: "${Object.keys(ReferenceEventTypes).join(', ')}"`);
throw new Error(
`Query.on failed: First argument must be a valid string event type: "${Object.keys(
ReferenceEventTypes
).join(', ')}"`
);
}
if (!callback) {
throw new Error('Query.on failed: Function called with 1 argument. Expects at least 2.');
throw new Error(
'Query.on failed: Function called with 1 argument. Expects at least 2.'
);
}
if (!isFunction(callback)) {
throw new Error('Query.on failed: Second argument must be a valid function.');
throw new Error(
'Query.on failed: Second argument must be a valid function.'
);
}
if (cancelCallbackOrContext && !isFunction(cancelCallbackOrContext) && !isObject(context) && !isObject(cancelCallbackOrContext)) {
throw new Error('Query.on failed: Function called with 3 arguments, but third optional argument `cancelCallbackOrContext` was not a function.');
if (
cancelCallbackOrContext &&
!isFunction(cancelCallbackOrContext) &&
!isObject(context) &&
!isObject(cancelCallbackOrContext)
) {
throw new Error(
'Query.on failed: Function called with 3 arguments, but third optional argument `cancelCallbackOrContext` was not a function.'
);
}
if (cancelCallbackOrContext && !isFunction(cancelCallbackOrContext) && context) {
throw new Error('Query.on failed: Function called with 4 arguments, but third optional argument `cancelCallbackOrContext` was not a function.');
if (
cancelCallbackOrContext &&
!isFunction(cancelCallbackOrContext) &&
context
) {
throw new Error(
'Query.on failed: Function called with 4 arguments, but third optional argument `cancelCallbackOrContext` was not a function.'
);
}
const eventRegistrationKey = this._getRegistrationKey(eventType);
const registrationCancellationKey = `${eventRegistrationKey}$cancelled`;
const _context = (cancelCallbackOrContext && !isFunction(cancelCallbackOrContext)) ? cancelCallbackOrContext : context;
const _context =
cancelCallbackOrContext && !isFunction(cancelCallbackOrContext)
? cancelCallbackOrContext
: context;
const registrationObj = {
eventType,
ref: this,
@ -677,7 +774,9 @@ export default class Reference extends ReferenceBase {
appName: this._database.app.name,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
listener: _context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
listener: _context
? cancelCallbackOrContext.bind(_context)
: cancelCallbackOrContext,
});
}
@ -724,18 +823,29 @@ export default class Reference extends ReferenceBase {
if (!arguments.length) {
// Firebase Docs:
// if no eventType or callback is specified, all callbacks for the Reference will be removed.
return SyncTree.removeListenersForRegistrations(SyncTree.getRegistrationsByPath(this.path));
return SyncTree.removeListenersForRegistrations(
SyncTree.getRegistrationsByPath(this.path)
);
}
/*
* VALIDATE ARGS
*/
if (eventType && (!isString(eventType) || !ReferenceEventTypes[eventType])) {
throw new Error(`Query.off failed: First argument must be a valid string event type: "${Object.keys(ReferenceEventTypes).join(', ')}"`);
if (
eventType &&
(!isString(eventType) || !ReferenceEventTypes[eventType])
) {
throw new Error(
`Query.off failed: First argument must be a valid string event type: "${Object.keys(
ReferenceEventTypes
).join(', ')}"`
);
}
if (originalCallback && !isFunction(originalCallback)) {
throw new Error('Query.off failed: Function called with 2 arguments, but second optional argument was not a function.');
throw new Error(
'Query.off failed: Function called with 2 arguments, but second optional argument was not a function.'
);
}
// Firebase Docs:
@ -745,7 +855,11 @@ export default class Reference extends ReferenceBase {
// remove the callback.
// Remove only a single registration
if (eventType && originalCallback) {
const registration = SyncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
const registration = SyncTree.getOneByPathEventListener(
this.path,
eventType,
originalCallback
);
if (!registration) return [];
// remove the paired cancellation registration if any exist
@ -753,15 +867,20 @@ export default class Reference extends ReferenceBase {
// remove only the first registration to match firebase web sdk
// call multiple times to remove multiple registrations
return SyncTree.removeListenerRegistrations(originalCallback, [registration]);
return SyncTree.removeListenerRegistrations(originalCallback, [
registration,
]);
}
// Firebase Docs:
// If a callback is not specified, all callbacks for the specified eventType will be removed.
const registrations = SyncTree.getRegistrationsByPathEvent(this.path, eventType);
const registrations = SyncTree.getRegistrationsByPathEvent(
this.path,
eventType
);
SyncTree.removeListenersForRegistrations(
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`)
);
return SyncTree.removeListenersForRegistrations(registrations);

View File

@ -3,7 +3,7 @@
* Snapshot representation wrapper
*/
import { isObject, deepGet, deepExists } from './../../utils';
import type Reference from './reference.js';
import type Reference from './reference';
/**
* @class DataSnapshot
@ -39,7 +39,8 @@ export default class Snapshot {
*/
val(): any {
// clone via JSON stringify/parse - prevent modification of this._value
if (isObject(this._value) || Array.isArray(this._value)) return JSON.parse(JSON.stringify(this._value));
if (isObject(this._value) || Array.isArray(this._value))
return JSON.parse(JSON.stringify(this._value));
return this._value;
}

View File

@ -14,9 +14,7 @@ let transactionId = 0;
* @returns {number}
* @private
*/
const generateTransactionId = (): number => {
return transactionId++;
};
const generateTransactionId = (): number => transactionId++;
/**
* @class TransactionHandler
@ -24,7 +22,7 @@ const generateTransactionId = (): number => {
export default class TransactionHandler {
_database: Database;
_transactionListener: Function;
_transactions: { [number]: Object }
_transactions: { [number]: Object };
constructor(database: Database) {
this._transactions = {};
@ -32,7 +30,7 @@ export default class TransactionHandler {
this._transactionListener = SharedEventEmitter.addListener(
getAppEventName(this._database, 'database_transaction_event'),
this._handleTransactionEvent.bind(this),
this._handleTransactionEvent.bind(this)
);
}
@ -43,7 +41,12 @@ export default class TransactionHandler {
* @param onComplete
* @param applyLocally
*/
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) {
add(
reference: Object,
transactionUpdater: Function,
onComplete?: Function,
applyLocally?: boolean = false
) {
const id = generateTransactionId();
this._transactions[id] = {
@ -56,7 +59,11 @@ export default class TransactionHandler {
started: true,
};
getNativeModule(this._database).transactionStart(reference.path, id, applyLocally);
getNativeModule(this._database).transactionStart(
reference.path,
id,
applyLocally
);
}
/**
@ -78,7 +85,10 @@ export default class TransactionHandler {
case 'complete':
return this._handleComplete(event);
default:
getLogger(this._database).warn(`Unknown transaction event type: '${event.type}'`, event);
getLogger(this._database).warn(
`Unknown transaction event type: '${event.type}'`,
event
);
return undefined;
}
}
@ -104,7 +114,10 @@ export default class TransactionHandler {
abort = true;
}
getNativeModule(this._database).transactionTryCommit(id, { value: newValue, abort });
getNativeModule(this._database).transactionTryCommit(id, {
value: newValue,
abort,
});
}
}
@ -137,7 +150,11 @@ export default class TransactionHandler {
if (transaction && !transaction.completed) {
transaction.completed = true;
try {
transaction.onComplete(null, event.committed, Object.assign({}, event.snapshot));
transaction.onComplete(
null,
event.committed,
Object.assign({}, event.snapshot)
);
} finally {
setImmediate(() => {
delete this._transactions[event.id];

View File

@ -14,6 +14,7 @@ export default class Crashlytics extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}

View File

@ -7,9 +7,18 @@ import Query from './Query';
import { firestoreAutoId } from '../../utils';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type {
FirestoreQueryDirection,
FirestoreQueryOperator,
} from '../../types';
import type FieldPath from './FieldPath';
import type Path from './Path';
import type { Observer, ObserverOnError, ObserverOnNext, QueryListenOptions } from './Query';
import type {
Observer,
ObserverOnError,
ObserverOnNext,
QueryListenOptions,
} from './Query';
import type QuerySnapshot from './QuerySnapshot';
/**
@ -36,13 +45,14 @@ export default class CollectionReference {
get parent(): DocumentReference | null {
const parentPath = this._collectionPath.parent();
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
return parentPath
? new DocumentReference(this._firestore, parentPath)
: null;
}
add(data: Object): Promise<DocumentReference> {
const documentRef = this.doc();
return documentRef.set(data)
.then(() => Promise.resolve(documentRef));
return documentRef.set(data).then(() => Promise.resolve(documentRef));
}
doc(documentPath?: string): DocumentReference {
@ -76,12 +86,19 @@ export default class CollectionReference {
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
onError?: ObserverOnError
): () => void {
return this._query.onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError);
return this._query.onSnapshot(
optionsOrObserverOrOnNext,
observerOrOnNextOrOnError,
onError
);
}
orderBy(fieldPath: string, directionStr?: FirestoreQueryDirection): Query {
orderBy(
fieldPath: string | FieldPath,
directionStr?: FirestoreQueryDirection
): Query {
return this._query.orderBy(fieldPath, directionStr);
}

View File

@ -4,6 +4,8 @@
*/
import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot';
import FieldPath from './FieldPath';
import { mergeFieldPathData } from './utils';
import { buildNativeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
@ -11,20 +13,23 @@ import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreWriteOptions } from '../../types';
import type {
FirestoreNativeDocumentSnapshot,
FirestoreWriteOptions,
} from '../../types';
import type Path from './Path';
type DocumentListenOptions = {
includeMetadataChanges: boolean,
}
};
type ObserverOnError = (Object) => void;
type ObserverOnNext = (DocumentSnapshot) => void;
type ObserverOnError = Object => void;
type ObserverOnNext = DocumentSnapshot => void;
type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
}
};
/**
* @class DocumentReference
@ -68,8 +73,7 @@ export default class DocumentReference {
}
delete(): Promise<void> {
return getNativeModule(this._firestore)
.documentDelete(this.path);
return getNativeModule(this._firestore).documentDelete(this.path);
}
get(): Promise<DocumentSnapshot> {
@ -79,28 +83,41 @@ export default class DocumentReference {
}
onSnapshot(
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | ObserverOnNext,
optionsOrObserverOrOnNext:
| DocumentListenOptions
| Observer
| ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
onError?: ObserverOnError
) {
let observer: Observer;
let docListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
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.'
);
}
// $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
if (optionsOrObserverOrOnNext.next) {
if (isFunction(optionsOrObserverOrOnNext.next)) {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
if (
optionsOrObserverOrOnNext.error &&
!isFunction(optionsOrObserverOrOnNext.error)
) {
throw new Error(
'DocumentReference.onSnapshot failed: Observer.error must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
@ -108,66 +125,103 @@ export default class DocumentReference {
error: optionsOrObserverOrOnNext.error,
};
} 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.'
);
}
} else if (optionsOrObserverOrOnNext.includeMetadataChanges) {
} else if (
Object.prototype.hasOwnProperty.call(
optionsOrObserverOrOnNext,
'includeMetadataChanges'
)
) {
docListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
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.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
// Called with Options, Observer
} else if (
observerOrOnNextOrOnError &&
isObject(observerOrOnNextOrOnError) &&
observerOrOnNextOrOnError.next
) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
if (
observerOrOnNextOrOnError.error &&
!isFunction(observerOrOnNextOrOnError.error)
) {
throw new Error(
'DocumentReference.onSnapshot failed: Observer.error must be a valid function.'
);
}
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} 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.'
);
}
} else {
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a function or observer.');
throw new Error(
'DocumentReference.onSnapshot failed: Second argument must be a function or observer.'
);
}
} else {
throw new Error('DocumentReference.onSnapshot failed: First argument must be a function, observer or options.');
throw new Error(
'DocumentReference.onSnapshot failed: First argument must be a function, observer or options.'
);
}
} else {
throw new Error('DocumentReference.onSnapshot failed: Called with invalid arguments.');
throw new Error(
'DocumentReference.onSnapshot failed: Called with invalid arguments.'
);
}
const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this.firestore, nativeDocumentSnapshot);
const listener = (
nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot
) => {
const documentSnapshot = new DocumentSnapshot(
this.firestore,
nativeDocumentSnapshot
);
observer.next(documentSnapshot);
};
// Listen to snapshot events
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener,
listener
);
// Listen for snapshot error events
if (observer.error) {
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
observer.error,
getAppEventName(
this._firestore,
`onDocumentSnapshotError:${listenerId}`
),
observer.error
);
}
// Add the native listener
getNativeModule(this._firestore)
.documentOnSnapshot(this.path, listenerId, docListenOptions);
getNativeModule(this._firestore).documentOnSnapshot(
this.path,
listenerId,
docListenOptions
);
// Return an unsubscribe method
return this._offDocumentSnapshot.bind(this, listenerId, listener);
@ -175,32 +229,47 @@ export default class DocumentReference {
set(data: Object, writeOptions?: FirestoreWriteOptions): Promise<void> {
const nativeData = buildNativeMap(data);
return getNativeModule(this._firestore)
.documentSet(this.path, nativeData, writeOptions);
return getNativeModule(this._firestore).documentSet(
this.path,
nativeData,
writeOptions
);
}
update(...args: any[]): Promise<void> {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('DocumentReference.update failed: If using a single argument, it must be an object.');
throw new Error(
'DocumentReference.update failed: If using a single argument, it must be an object.'
);
}
// eslint-disable-next-line prefer-destructuring
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.');
throw new Error(
'DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.'
);
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
const value = args[i + 1];
if (!isString(key)) {
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string`);
if (isString(key)) {
data[key] = value;
} else if (key instanceof FieldPath) {
data = mergeFieldPathData(data, key._segments, value);
} else {
throw new Error(
`DocumentReference.update failed: Argument at index ${i} must be a string or FieldPath`
);
}
data[key] = value;
}
}
const nativeData = buildNativeMap(data);
return getNativeModule(this._firestore)
.documentUpdate(this.path, nativeData);
return getNativeModule(this._firestore).documentUpdate(
this.path,
nativeData
);
}
/**
@ -213,9 +282,14 @@ export default class DocumentReference {
*/
_offDocumentSnapshot(listenerId: string, listener: Function) {
getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.documentOffSnapshot(this.path, listenerId);
SharedEventEmitter.removeListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener
);
SharedEventEmitter.removeListener(
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
listener
);
getNativeModule(this._firestore).documentOffSnapshot(this.path, listenerId);
}
}

View File

@ -3,11 +3,27 @@
* DocumentSnapshot representation wrapper
*/
import DocumentReference from './DocumentReference';
import FieldPath from './FieldPath';
import Path from './Path';
import { isObject } from '../../utils';
import { parseNativeMap } from './utils/serialize';
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
import type {
FirestoreNativeDocumentSnapshot,
FirestoreSnapshotMetadata,
} from '../../types';
const extractFieldPathData = (data: Object | void, segments: string[]): any => {
if (!data || !isObject(data)) {
return undefined;
}
const pathValue = data[segments[0]];
if (segments.length === 1) {
return pathValue;
}
return extractFieldPathData(pathValue, segments.slice(1));
};
/**
* @class DocumentSnapshot
@ -17,10 +33,16 @@ export default class DocumentSnapshot {
_metadata: FirestoreSnapshotMetadata;
_ref: DocumentReference;
constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentSnapshot) {
constructor(
firestore: Firestore,
nativeData: FirestoreNativeDocumentSnapshot
) {
this._data = parseNativeMap(firestore, nativeData.data);
this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
this._ref = new DocumentReference(
firestore,
Path.fromName(nativeData.path)
);
}
get exists(): boolean {
@ -43,7 +65,10 @@ export default class DocumentSnapshot {
return this._data;
}
get(fieldPath: string): any {
get(fieldPath: string | FieldPath): any {
if (fieldPath instanceof FieldPath) {
return extractFieldPathData(this._data, fieldPath._segments);
}
return this._data ? this._data[fieldPath] : undefined;
}
}

View File

@ -0,0 +1,22 @@
/**
* @flow
* FieldPath representation wrapper
*/
/**
* @class FieldPath
*/
export default class FieldPath {
_segments: string[];
constructor(...segments: string[]) {
// TODO: Validation
this._segments = segments;
}
static documentId(): FieldPath {
return DOCUMENT_ID;
}
}
export const DOCUMENT_ID = new FieldPath('__name__');

View File

@ -48,7 +48,7 @@ export default class Path {
*
* @package
*/
static fromName(name): Path {
static fromName(name: string): Path {
const parts = name.split('/');
if (parts.length === 0) {

View File

@ -3,6 +3,7 @@
* Query representation wrapper
*/
import DocumentSnapshot from './DocumentSnapshot';
import FieldPath from './FieldPath';
import QuerySnapshot from './QuerySnapshot';
import { buildNativeArray, buildTypeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
@ -11,7 +12,10 @@ import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type {
FirestoreQueryDirection,
FirestoreQueryOperator,
} from '../../types';
import type Path from './Path';
const DIRECTIONS: { [FirestoreQueryDirection]: string } = {
@ -30,15 +34,20 @@ const OPERATORS: { [FirestoreQueryOperator]: string } = {
'<=': 'LESS_THAN_OR_EQUAL',
};
type FieldFilter = {
fieldPath: string,
type NativeFieldPath = {|
elements?: string[],
string?: string,
type: 'fieldpath' | 'string',
|};
type FieldFilter = {|
fieldPath: NativeFieldPath,
operator: string,
value: any,
}
type FieldOrder = {
|};
type FieldOrder = {|
direction: string,
fieldPath: string,
}
fieldPath: NativeFieldPath,
|};
type QueryOptions = {
endAt?: any[],
endBefore?: any[],
@ -47,20 +56,35 @@ type QueryOptions = {
selectFields?: string[],
startAfter?: any[],
startAt?: any[],
}
};
export type QueryListenOptions = {
export type QueryListenOptions = {|
includeDocumentMetadataChanges: boolean,
includeQueryMetadataChanges: boolean,
}
|};
export type ObserverOnError = (Object) => void;
export type ObserverOnNext = (QuerySnapshot) => void;
export type ObserverOnError = Object => void;
export type ObserverOnNext = QuerySnapshot => void;
export type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
}
};
const buildNativeFieldPath = (
fieldPath: string | FieldPath
): NativeFieldPath => {
if (fieldPath instanceof FieldPath) {
return {
elements: fieldPath._segments,
type: 'fieldpath',
};
}
return {
string: fieldPath,
type: 'string',
};
};
/**
* @class Query
@ -76,10 +100,9 @@ export default class Query {
constructor(
firestore: Firestore,
path: Path,
fieldFilters?:
FieldFilter[],
fieldFilters?: FieldFilter[],
fieldOrders?: FieldOrder[],
queryOptions?: QueryOptions,
queryOptions?: QueryOptions
) {
this._fieldFilters = fieldFilters || [];
this._fieldOrders = fieldOrders || [];
@ -103,7 +126,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -118,7 +141,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -128,7 +151,7 @@ export default class Query {
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
this._queryOptions
)
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
}
@ -146,33 +169,43 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
onError?: ObserverOnError
) {
let observer: Observer;
let queryListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
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.'
);
}
// $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
if (optionsOrObserverOrOnNext.next) {
if (isFunction(optionsOrObserverOrOnNext.next)) {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
if (
optionsOrObserverOrOnNext.error &&
!isFunction(optionsOrObserverOrOnNext.error)
) {
throw new Error(
'Query.onSnapshot failed: Observer.error must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
@ -180,91 +213,129 @@ export default class Query {
error: optionsOrObserverOrOnNext.error,
};
} 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.'
);
}
} else if (optionsOrObserverOrOnNext.includeDocumentMetadataChanges || optionsOrObserverOrOnNext.includeQueryMetadataChanges) {
} else if (
Object.prototype.hasOwnProperty.call(
optionsOrObserverOrOnNext,
'includeDocumentMetadataChanges'
) ||
Object.prototype.hasOwnProperty.call(
optionsOrObserverOrOnNext,
'includeQueryMetadataChanges'
)
) {
queryListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
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.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
// Called with Options, Observer
} else if (
observerOrOnNextOrOnError &&
isObject(observerOrOnNextOrOnError) &&
observerOrOnNextOrOnError.next
) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
if (
observerOrOnNextOrOnError.error &&
!isFunction(observerOrOnNextOrOnError.error)
) {
throw new Error(
'Query.onSnapshot failed: Observer.error must be a valid function.'
);
}
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} 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.'
);
}
} else {
throw new Error('Query.onSnapshot failed: Second argument must be a function or observer.');
throw new Error(
'Query.onSnapshot failed: Second argument must be a function or observer.'
);
}
} else {
throw new Error('Query.onSnapshot failed: First argument must be a function, observer or options.');
throw new Error(
'Query.onSnapshot failed: First argument must be a function, observer or options.'
);
}
} 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 listener = (nativeQuerySnapshot) => {
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
const listener = nativeQuerySnapshot => {
const querySnapshot = new QuerySnapshot(
this._firestore,
this,
nativeQuerySnapshot
);
observer.next(querySnapshot);
};
// Listen to snapshot events
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
listener,
listener
);
// Listen for snapshot error events
if (observer.error) {
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
observer.error,
observer.error
);
}
// Add the native listener
getNativeModule(this._firestore)
.collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
queryListenOptions,
);
getNativeModule(this._firestore).collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
queryListenOptions
);
// Return an unsubscribe method
return this._offCollectionSnapshot.bind(this, listenerId, listener);
}
orderBy(fieldPath: string, directionStr?: FirestoreQueryDirection = 'asc'): Query {
orderBy(
fieldPath: string | FieldPath,
directionStr?: FirestoreQueryDirection = 'asc'
): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isOptionalFieldOrder('directionStr', directionStr);
if (this._queryOptions.startAt || this._queryOptions.endAt) {
throw new Error('Cannot specify an orderBy() constraint after calling ' +
'startAt(), startAfter(), endBefore() or endAt().');
throw new Error(
'Cannot specify an orderBy() constraint after calling ' +
'startAt(), startAfter(), endBefore() or endAt().'
);
}
const newOrder = {
const newOrder: FieldOrder = {
direction: DIRECTIONS[directionStr],
fieldPath,
fieldPath: buildNativeFieldPath(fieldPath),
};
const combinedOrders = this._fieldOrders.concat(newOrder);
return new Query(
@ -272,7 +343,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
combinedOrders,
this._queryOptions,
this._queryOptions
);
}
@ -287,7 +358,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -302,17 +373,21 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
where(fieldPath: string, opStr: FirestoreQueryOperator, value: any): Query {
where(
fieldPath: string | FieldPath,
opStr: FirestoreQueryOperator,
value: any
): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isFieldFilter('fieldFilter', opStr, value);
const nativeValue = buildTypeMap(value);
const newFilter = {
fieldPath,
const newFilter: FieldFilter = {
fieldPath: buildNativeFieldPath(fieldPath),
operator: OPERATORS[opStr],
value: nativeValue,
};
@ -322,7 +397,7 @@ export default class Query {
this._referencePath,
combinedFilters,
this._fieldOrders,
this._queryOptions,
this._queryOptions
);
}
@ -333,12 +408,23 @@ export default class Query {
_buildOrderByOption(snapshotOrVarArgs: any[]) {
// TODO: Validation
let values;
if (snapshotOrVarArgs.length === 1 && snapshotOrVarArgs[0] instanceof DocumentSnapshot) {
const docSnapshot = snapshotOrVarArgs[0];
if (
snapshotOrVarArgs.length === 1 &&
snapshotOrVarArgs[0] instanceof DocumentSnapshot
) {
const docSnapshot: DocumentSnapshot = snapshotOrVarArgs[0];
values = [];
for (let i = 0; i < this._fieldOrders.length; i++) {
const fieldOrder = this._fieldOrders[i];
values.push(docSnapshot.get(fieldOrder.fieldPath));
if (
fieldOrder.fieldPath.type === 'string' &&
fieldOrder.fieldPath.string
) {
values.push(docSnapshot.get(fieldOrder.fieldPath.string));
} else if (fieldOrder.fieldPath.fieldpath) {
const fieldPath = new FieldPath(...fieldOrder.fieldPath.fieldpath);
values.push(docSnapshot.get(fieldPath));
}
}
} else {
values = snapshotOrVarArgs;
@ -353,15 +439,20 @@ export default class Query {
*/
_offCollectionSnapshot(listenerId: string, listener: Function) {
getLogger(this._firestore).info('Removing onQuerySnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.collectionOffSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
);
SharedEventEmitter.removeListener(
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
listener
);
SharedEventEmitter.removeListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
listener
);
getNativeModule(this._firestore).collectionOffSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId
);
}
}

View File

@ -6,14 +6,18 @@ import DocumentChange from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot';
import type Firestore from './';
import type { FirestoreNativeDocumentChange, FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
import type {
FirestoreNativeDocumentChange,
FirestoreNativeDocumentSnapshot,
FirestoreSnapshotMetadata,
} from '../../types';
import type Query from './Query';
type QuerySnapshotNativeData = {
changes: FirestoreNativeDocumentChange[],
documents: FirestoreNativeDocumentSnapshot[],
metadata: FirestoreSnapshotMetadata,
}
};
/**
* @class QuerySnapshot
@ -24,9 +28,17 @@ export default class QuerySnapshot {
_metadata: FirestoreSnapshotMetadata;
_query: Query;
constructor(firestore: Firestore, query: Query, nativeData: QuerySnapshotNativeData) {
this._changes = nativeData.changes.map(change => new DocumentChange(firestore, change));
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
constructor(
firestore: Firestore,
query: Query,
nativeData: QuerySnapshotNativeData
) {
this._changes = nativeData.changes.map(
change => new DocumentChange(firestore, change)
);
this._docs = nativeData.documents.map(
doc => new DocumentSnapshot(firestore, doc)
);
this._metadata = nativeData.metadata;
this._query = query;
}
@ -59,7 +71,7 @@ export default class QuerySnapshot {
// TODO: Validation
// validate.isFunction('callback', callback);
this._docs.forEach((doc) => {
this._docs.forEach(doc => {
callback(doc);
});
}

View File

@ -2,6 +2,8 @@
* @flow
* WriteBatch representation wrapper
*/
import FieldPath from './FieldPath';
import { mergeFieldPathData } from './utils';
import { buildNativeMap } from './utils/serialize';
import { isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
@ -15,7 +17,7 @@ type DocumentWrite = {
options?: Object,
path: string,
type: 'DELETE' | 'SET' | 'UPDATE',
}
};
/**
* @class WriteBatch
@ -45,7 +47,11 @@ export default class WriteBatch {
return this;
}
set(docRef: DocumentReference, data: Object, writeOptions?: FirestoreWriteOptions) {
set(
docRef: DocumentReference,
data: Object,
writeOptions?: FirestoreWriteOptions
) {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data);
@ -67,19 +73,29 @@ export default class WriteBatch {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('DocumentReference.update failed: If using two arguments, the second must be an object.');
throw new Error(
'WriteBatch.update failed: If using two arguments, the second must be an object.'
);
}
// eslint-disable-next-line prefer-destructuring
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('DocumentReference.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
throw new Error(
'WriteBatch.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.'
);
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
const value = args[i + 1];
if (!isString(key)) {
throw new Error(`DocumentReference.update failed: Argument at index ${i + 1} must be a string`);
if (isString(key)) {
data[key] = value;
} else if (key instanceof FieldPath) {
data = mergeFieldPathData(data, key._segments, value);
} else {
throw new Error(
`WriteBatch.update failed: Argument at index ${i} must be a string or FieldPath`
);
}
data[key] = value;
}
}

View File

@ -8,6 +8,7 @@ import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference';
import FieldPath from './FieldPath';
import FieldValue from './FieldValue';
import GeoPoint from './GeoPoint';
import Path from './Path';
@ -24,7 +25,7 @@ type CollectionSyncEvent = {
error?: Object,
listenerId: string,
path: string,
}
};
type DocumentSyncEvent = {
appName: string,
@ -32,7 +33,7 @@ type DocumentSyncEvent = {
error?: Object,
listenerId: string,
path: string,
}
};
const NATIVE_EVENTS = [
'firestore_collection_sync_event',
@ -52,6 +53,7 @@ export default class Firestore extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
this._referencePath = new Path([]);
@ -60,14 +62,14 @@ export default class Firestore extends ModuleBase {
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
this._onCollectionSyncEvent.bind(this)
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onDocumentSnapshot
getAppEventName(this, 'firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this),
this._onDocumentSyncEvent.bind(this)
);
}
@ -112,7 +114,12 @@ export default class Firestore extends ModuleBase {
}
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(): void {
@ -126,9 +133,15 @@ export default class Firestore extends ModuleBase {
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
event.error
);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshot:${event.listenerId}`),
event.querySnapshot
);
}
}
@ -139,14 +152,21 @@ export default class Firestore extends ModuleBase {
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
event.error
);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`),
event.documentSnapshot
);
}
}
}
export const statics = {
FieldPath,
FieldValue,
GeoPoint,
enableLogging(enabled: boolean) {

View File

@ -0,0 +1,41 @@
/**
* @flow
*/
const buildFieldPathData = (segments: string[], value: any): Object => {
if (segments.length === 1) {
return {
[segments[0]]: value,
};
}
return {
[segments[0]]: buildFieldPathData(segments.slice(1), value),
};
};
// eslint-disable-next-line import/prefer-default-export
export const mergeFieldPathData = (
data: Object,
segments: string[],
value: any
): Object => {
if (segments.length === 1) {
return {
...data,
[segments[0]]: value,
};
} else if (data[segments[0]]) {
return {
...data,
[segments[0]]: mergeFieldPathData(
data[segments[0]],
segments.slice(1),
value
),
};
}
return {
...data,
[segments[0]]: buildFieldPathData(segments.slice(1), value),
};
};

View File

@ -3,7 +3,11 @@
*/
import DocumentReference from '../DocumentReference';
import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
import { DOCUMENT_ID } from '../FieldPath';
import {
DELETE_FIELD_VALUE,
SERVER_TIMESTAMP_FIELD_VALUE,
} from '../FieldValue';
import GeoPoint from '../GeoPoint';
import Path from '../Path';
import { typeOf } from '../../../utils';
@ -17,10 +21,12 @@ import type { FirestoreTypeMap } from '../../../types';
* for transmission to the native side
*/
export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } => {
export const buildNativeMap = (
data: Object
): { [string]: FirestoreTypeMap } => {
const nativeData = {};
if (data) {
Object.keys(data).forEach((key) => {
Object.keys(data).forEach(key => {
const typeMap = buildTypeMap(data[key]);
if (typeMap) {
nativeData[key] = typeMap;
@ -33,7 +39,7 @@ export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } =>
export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => {
const nativeArray = [];
if (array) {
array.forEach((value) => {
array.forEach(value => {
const typeMap = buildTypeMap(value);
if (typeMap) {
nativeArray.push(typeMap);
@ -60,6 +66,11 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
type: 'fieldvalue',
value: 'timestamp',
};
} else if (value === DOCUMENT_ID) {
return {
type: 'documentid',
value: null,
};
} else if (type === 'boolean' || type === 'number' || type === 'string') {
return {
type,
@ -104,21 +115,27 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
* side and converts to the correct Firestore JS types
*/
export const parseNativeMap = (firestore: Firestore, nativeData: { [string]: FirestoreTypeMap }): Object | void => {
export const parseNativeMap = (
firestore: Firestore,
nativeData: { [string]: FirestoreTypeMap }
): Object | void => {
let data;
if (nativeData) {
data = {};
Object.keys(nativeData).forEach((key) => {
Object.keys(nativeData).forEach(key => {
data[key] = parseTypeMap(firestore, nativeData[key]);
});
}
return data;
};
const parseNativeArray = (firestore: Firestore, nativeArray: FirestoreTypeMap[]): any[] => {
const parseNativeArray = (
firestore: Firestore,
nativeArray: FirestoreTypeMap[]
): any[] => {
const array = [];
if (nativeArray) {
nativeArray.forEach((typeMap) => {
nativeArray.forEach(typeMap => {
array.push(parseTypeMap(firestore, typeMap));
});
}

View File

@ -13,9 +13,7 @@ const EVENT_TYPE = {
Link: 'dynamic_link_received',
};
const NATIVE_EVENTS = [
EVENT_TYPE.Link,
];
const NATIVE_EVENTS = [EVENT_TYPE.Link];
export const MODULE_NAME = 'RNFirebaseLinks';
export const NAMESPACE = 'links';
@ -59,10 +57,16 @@ function checkForMandatoryParameters(parameters: Object): void {
if (!isString(parameters.link)) {
throw new Error('No link was specified.');
}
if (isObject(parameters.androidInfo) && !isString(parameters.androidInfo.androidPackageName)) {
if (
isObject(parameters.androidInfo) &&
!isString(parameters.androidInfo.androidPackageName)
) {
throw new Error('No androidPackageName was specified.');
}
if (isObject(parameters.iosInfo) && !isString(parameters.iosInfo.iosBundleId)) {
if (
isObject(parameters.iosInfo) &&
!isString(parameters.iosInfo.iosBundleId)
) {
throw new Error('No iosBundleId was specified.');
}
}
@ -75,6 +79,7 @@ export default class Links extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
@ -97,7 +102,10 @@ export default class Links extends ModuleBase {
* @returns {Function}
*/
onLink(listener: Function): () => any {
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.Link, listener);
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Link,
listener
);
return () => rnListener.remove();
}

View File

@ -12,7 +12,9 @@ export default class RemoteMessage {
id: generatePushID(),
ttl: 3600,
// add the googleapis sender id part if not already added.
sender: `${sender}`.includes('@') ? sender : `${sender}@gcm.googleapis.com`,
sender: `${sender}`.includes('@')
? sender
: `${sender}@gcm.googleapis.com`,
type: 'remote',
data: {},
};
@ -57,7 +59,6 @@ export default class RemoteMessage {
return this;
}
/**
*
* @param data
@ -65,7 +66,9 @@ export default class RemoteMessage {
*/
setData(data: Object = {}) {
if (!isObject(data)) {
throw new Error(`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`);
throw new Error(
`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`
);
}
const props = Object.keys(data);

View File

@ -33,10 +33,7 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const NATIVE_EVENTS = [
EVENT_TYPE.RefreshToken,
EVENT_TYPE.Notification,
];
const NATIVE_EVENTS = [EVENT_TYPE.RefreshToken, EVENT_TYPE.Notification];
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
@ -49,7 +46,6 @@ function finish(data) {
return;
}
if (!this._finishCalled && this._completionHandlerId) {
let result = data;
@ -59,21 +55,35 @@ function finish(data) {
case NOTIFICATION_TYPE.Remote:
result = result || REMOTE_NOTIFICATION_RESULT.NoData;
if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) {
throw new Error('Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT');
throw new Error(
'Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT'
);
}
FirebaseMessaging.finishRemoteNotification(this._completionHandlerId, result);
FirebaseMessaging.finishRemoteNotification(
this._completionHandlerId,
result
);
return;
case NOTIFICATION_TYPE.NotificationResponse:
FirebaseMessaging.finishNotificationResponse(this._completionHandlerId);
return;
case NOTIFICATION_TYPE.WillPresent:
result = result || (this.show_in_foreground ? WILL_PRESENT_RESULT.All : WILL_PRESENT_RESULT.None);
result =
result ||
(this.show_in_foreground
? WILL_PRESENT_RESULT.All
: WILL_PRESENT_RESULT.None);
if (!Object.values(WILL_PRESENT_RESULT).includes(result)) {
throw new Error('Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT');
throw new Error(
'Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT'
);
}
FirebaseMessaging.finishWillPresentNotification(this._completionHandlerId, result);
FirebaseMessaging.finishWillPresentNotification(
this._completionHandlerId,
result
);
break;
default:
}
@ -91,6 +101,7 @@ export default class Messaging extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
@ -154,7 +165,10 @@ export default class Messaging extends ModuleBase {
*/
scheduleLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
if (!notification.id) return Promise.reject(new Error('An id is required to schedule a local notification.'));
if (!notification.id)
return Promise.reject(
new Error('An id is required to schedule a local notification.')
);
_notification.local_notification = true;
return getNativeModule(this).scheduleLocalNotification(_notification);
}
@ -187,7 +201,8 @@ export default class Messaging extends ModuleBase {
*/
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return getNativeModule(this).removeAllDeliveredNotifications();
if (id === '*')
return getNativeModule(this).removeAllDeliveredNotifications();
return getNativeModule(this).removeDeliveredNotification(id);
}
@ -200,7 +215,6 @@ export default class Messaging extends ModuleBase {
return getNativeModule(this).requestPermissions();
}
/**
* Set notification count badge number
* @param n
@ -222,10 +236,10 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onMessage(listener: (Object) => any): () => any {
onMessage(listener: Object => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Notification,
async (event) => {
async event => {
const data = {
...event,
finish,
@ -235,7 +249,7 @@ export default class Messaging extends ModuleBase {
if (!data._finishCalled) {
data.finish();
}
},
}
);
return () => rnListener.remove();
}
@ -245,8 +259,11 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onTokenRefresh(listener: (string) => any): () => any {
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
onTokenRefresh(listener: string => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.RefreshToken,
listener
);
return () => rnListener.remove();
}
@ -272,7 +289,9 @@ export default class Messaging extends ModuleBase {
*/
send(remoteMessage: RemoteMessage): Promise<void> {
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 getNativeModule(this).send(remoteMessage.toJSON());

View File

@ -15,6 +15,7 @@ export default class PerformanceMonitoring extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}

View File

@ -14,10 +14,7 @@ import type App from '../core/firebase-app';
const FirebaseStorage = NativeModules.RNFirebaseStorage;
const NATIVE_EVENTS = [
'storage_event',
'storage_error',
];
const NATIVE_EVENTS = ['storage_event', 'storage_error'];
export const MODULE_NAME = 'RNFirebaseStorage';
export const NAMESPACE = 'storage';
@ -32,17 +29,18 @@ export default class Storage extends ModuleBase {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_event'),
this._handleStorageEvent.bind(this),
this._handleStorageEvent.bind(this)
);
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_error'),
this._handleStorageEvent.bind(this),
this._handleStorageEvent.bind(this)
);
}
@ -117,12 +115,23 @@ export default class Storage extends ModuleBase {
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_addListener(path: string, eventName: string, cb: (evt: Object) => Object): void {
_addListener(
path: string,
eventName: string,
cb: (evt: Object) => Object
): void {
SharedEventEmitter.addListener(this._getSubEventName(path, eventName), cb);
}
_removeListener(path: string, eventName: string, origCB: (evt: Object) => Object): void {
SharedEventEmitter.removeListener(this._getSubEventName(path, eventName), origCB);
_removeListener(
path: string,
eventName: string,
origCB: (evt: Object) => Object
): void {
SharedEventEmitter.removeListener(
this._getSubEventName(path, eventName),
origCB
);
}
}
@ -137,15 +146,18 @@ export const statics = {
CANCELLED: 'cancelled',
ERROR: 'error',
},
Native: FirebaseStorage ? {
MAIN_BUNDLE_PATH: FirebaseStorage.MAIN_BUNDLE_PATH,
CACHES_DIRECTORY_PATH: FirebaseStorage.CACHES_DIRECTORY_PATH,
DOCUMENT_DIRECTORY_PATH: FirebaseStorage.DOCUMENT_DIRECTORY_PATH,
EXTERNAL_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_DIRECTORY_PATH,
EXTERNAL_STORAGE_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_STORAGE_DIRECTORY_PATH,
TEMP_DIRECTORY_PATH: FirebaseStorage.TEMP_DIRECTORY_PATH,
LIBRARY_DIRECTORY_PATH: FirebaseStorage.LIBRARY_DIRECTORY_PATH,
FILETYPE_REGULAR: FirebaseStorage.FILETYPE_REGULAR,
FILETYPE_DIRECTORY: FirebaseStorage.FILETYPE_DIRECTORY,
} : {},
Native: FirebaseStorage
? {
MAIN_BUNDLE_PATH: FirebaseStorage.MAIN_BUNDLE_PATH,
CACHES_DIRECTORY_PATH: FirebaseStorage.CACHES_DIRECTORY_PATH,
DOCUMENT_DIRECTORY_PATH: FirebaseStorage.DOCUMENT_DIRECTORY_PATH,
EXTERNAL_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_DIRECTORY_PATH,
EXTERNAL_STORAGE_DIRECTORY_PATH:
FirebaseStorage.EXTERNAL_STORAGE_DIRECTORY_PATH,
TEMP_DIRECTORY_PATH: FirebaseStorage.TEMP_DIRECTORY_PATH,
LIBRARY_DIRECTORY_PATH: FirebaseStorage.LIBRARY_DIRECTORY_PATH,
FILETYPE_REGULAR: FirebaseStorage.FILETYPE_REGULAR,
FILETYPE_DIRECTORY: FirebaseStorage.FILETYPE_DIRECTORY,
}
: {},
};

View File

@ -7,7 +7,6 @@ import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
import { getNativeModule } from '../../utils/native';
import type Storage from './';
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
*/
@ -75,7 +74,11 @@ export default class StorageReference extends ReferenceBase {
* @return {Promise}
*/
downloadFile(filePath: string): Promise<Object> {
return new StorageTask(DOWNLOAD_TASK, getNativeModule(this._storage).downloadFile(this.path, filePath), this);
return new StorageTask(
DOWNLOAD_TASK,
getNativeModule(this._storage).downloadFile(this.path, filePath),
this
);
}
/**
@ -94,6 +97,10 @@ export default class StorageReference extends ReferenceBase {
*/
putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
const _filePath = filePath.replace('file://', '');
return new StorageTask(UPLOAD_TASK, getNativeModule(this._storage).putFile(this.path, _filePath, metadata), this);
return new StorageTask(
UPLOAD_TASK,
getNativeModule(this._storage).putFile(this.path, _filePath, metadata),
this
);
}
}

View File

@ -12,31 +12,33 @@ export const DOWNLOAD_TASK = 'download';
declare type UploadTaskSnapshotType = {
bytesTransferred: number,
downloadURL: string|null,
downloadURL: string | null,
metadata: Object, // TODO flow type def for https://firebase.google.com/docs/reference/js/firebase.storage.FullMetadata.html
ref: StorageReference,
state: (
typeof StorageStatics.TaskState.RUNNING
state:
| typeof StorageStatics.TaskState.RUNNING
| typeof StorageStatics.TaskState.PAUSED
| typeof StorageStatics.TaskState.SUCCESS
| typeof StorageStatics.TaskState.CANCELLED
| typeof StorageStatics.TaskState.ERROR
),
| typeof StorageStatics.TaskState.ERROR,
task: StorageTask,
totalBytes: number,
};
declare type FuncSnapshotType = null|(snapshot: UploadTaskSnapshotType) => any;
declare type FuncSnapshotType =
| null
| ((snapshot: UploadTaskSnapshotType) => any);
declare type FuncErrorType = null|(error: Error) => any;
declare type FuncErrorType = null | ((error: Error) => any);
declare type NextOrObserverType = null |
{
next?: FuncSnapshotType,
error?: FuncErrorType,
complete?:FuncSnapshotType
} |
FuncSnapshotType;
declare type NextOrObserverType =
| null
| {
next?: FuncSnapshotType,
error?: FuncErrorType,
complete?: FuncSnapshotType,
}
| FuncSnapshotType;
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask
@ -49,7 +51,11 @@ export default class StorageTask {
then: () => 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.ref = storageRef;
this.storage = storageRef._storage;
@ -66,9 +72,9 @@ export default class StorageTask {
* @returns {Promise.<T>}
* @private
*/
_interceptSnapshotEvent(f: ?Function): null | () => * {
_interceptSnapshotEvent(f: ?Function): null | (() => *) {
if (!isFunction(f)) return null;
return (snapshot) => {
return snapshot => {
const _snapshot = Object.assign({}, snapshot);
_snapshot.task = this;
_snapshot.ref = this.ref;
@ -82,9 +88,9 @@ export default class StorageTask {
* @returns {*}
* @private
*/
_interceptErrorEvent(f: ?Function): null | (Error) => * {
_interceptErrorEvent(f: ?Function): null | (Error => *) {
if (!isFunction(f)) return null;
return (error) => {
return error => {
const _error = new Error(error.message);
// $FlowFixMe
_error.code = error.code;
@ -100,7 +106,11 @@ export default class StorageTask {
* @returns {function()}
* @private
*/
_subscribe(nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): Function {
_subscribe(
nextOrObserver: NextOrObserverType,
error: FuncErrorType,
complete: FuncSnapshotType
): Function {
let _error;
let _next;
let _complete;
@ -119,28 +129,31 @@ export default class StorageTask {
this.storage._addListener(
this.path,
StorageStatics.TaskEvent.STATE_CHANGED,
_next,
_next
);
}
if (_error) {
this.storage._addListener(
this.path,
`${this.type}_failure`,
_error,
);
this.storage._addListener(this.path, `${this.type}_failure`, _error);
}
if (_complete) {
this.storage._addListener(
this.path,
`${this.type}_success`,
_complete,
);
this.storage._addListener(this.path, `${this.type}_success`, _complete);
}
return () => {
if (_next) this.storage._removeListener(this.path, StorageStatics.TaskEvent.STATE_CHANGED, _next);
if (_error) this.storage._removeListener(this.path, `${this.type}_failure`, _error);
if (_complete) this.storage._removeListener(this.path, `${this.type}_success`, _complete);
if (_next)
this.storage._removeListener(
this.path,
StorageStatics.TaskEvent.STATE_CHANGED,
_next
);
if (_error)
this.storage._removeListener(this.path, `${this.type}_failure`, _error);
if (_complete)
this.storage._removeListener(
this.path,
`${this.type}_success`,
_complete
);
};
}
@ -152,13 +165,24 @@ export default class StorageTask {
* @param complete
* @returns {function()}
*/
on(event: string = StorageStatics.TaskEvent.STATE_CHANGED, nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): Function {
on(
event: string = StorageStatics.TaskEvent.STATE_CHANGED,
nextOrObserver: NextOrObserverType,
error: FuncErrorType,
complete: FuncSnapshotType
): Function {
if (!event) {
throw new Error('StorageTask.on listener is missing required string argument \'event\'.');
throw new Error(
"StorageTask.on listener is missing required string argument 'event'."
);
}
if (event !== StorageStatics.TaskEvent.STATE_CHANGED) {
throw new Error(`StorageTask.on event argument must be a string with a value of '${StorageStatics.TaskEvent.STATE_CHANGED}'`);
throw new Error(
`StorageTask.on event argument must be a string with a value of '${
StorageStatics.TaskEvent.STATE_CHANGED
}'`
);
}
// if only event provided return the subscriber function
@ -170,16 +194,22 @@ export default class StorageTask {
}
pause() {
throw new Error('.pause() is not currently supported by react-native-firebase');
throw new Error(
'.pause() is not currently supported by react-native-firebase'
);
}
resume() {
// todo
throw new Error('.resume() is not currently supported by react-native-firebase');
throw new Error(
'.resume() is not currently supported by react-native-firebase'
);
}
cancel() {
// todo
throw new Error('.cancel() is not currently supported by react-native-firebase');
throw new Error(
'.cancel() is not currently supported by react-native-firebase'
);
}
}

View File

@ -1,11 +1,9 @@
// @flow
import { NativeModules } from 'react-native';
// import { version as ReactVersion } from 'react';
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion';
import INTERNALS from '../../utils/internals';
import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
import PACKAGE from '../../../package.json';
import type App from '../core/firebase-app';
const FirebaseCoreModule = NativeModules.RNFirebase;
@ -14,13 +12,21 @@ type GoogleApiAvailabilityType = {
isAvailable: boolean,
isUserResolvableError?: boolean,
hasResolution?: boolean,
error?: string
}
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,
});
}
/**
*
*/
@ -30,12 +36,16 @@ export default class RNFirebaseUtils extends ModuleBase {
const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
if (
INTERNALS.OPTIONS.promptOnMissingPlayServices &&
this.playServicesAvailability.isUserResolvableError
) {
this.promptForPlayServices();
} else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
if (status === 2) console.warn(error); // only warn if it exists but may need an update
if (status === 2)
console.warn(error); // only warn if it exists but may need an update
else throw new Error(error);
} else {
console.warn(error);
@ -74,7 +84,12 @@ export default class RNFirebaseUtils extends ModuleBase {
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
*/
get playServicesAvailability(): GoogleApiAvailabilityType {
return FirebaseCoreModule.playServicesAvailability || { isAvailable: true, status: 0 };
return (
FirebaseCoreModule.playServicesAvailability || {
isAvailable: true,
status: 0,
}
);
}
/**
@ -96,11 +111,4 @@ export default class RNFirebaseUtils extends ModuleBase {
}
}
export const statics = {
// VERSIONS: {
// react: ReactVersion,
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),
// 'react-native-firebase': PACKAGE.version,
// },
};
export const statics = {};

View File

@ -35,24 +35,46 @@ export type FirebaseError = {
stack: string,
path: string,
details: string,
modifiers: 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 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'
export type FirebaseNamespace =
| 'admob'
| 'analytics'
| 'auth'
| 'config'
| 'crash'
| 'crashlytics'
| 'database'
| 'firestore'
| 'links'
| 'messaging'
| 'perf'
| 'storage'
| 'utils';
export type FirebaseOptions = {
@ -62,7 +84,7 @@ export type FirebaseOptions = {
messagingSenderId: string,
projectId: string,
storageBucket: string,
}
};
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {
(): M,
@ -92,12 +114,6 @@ export type ConfigModule = {
nativeModuleExists: boolean,
} & ConfigStatics;
export type AuthCredential = {
providerId: string,
token: string,
secret: string
}
/* Auth types */
export type AuthModule = {
@ -105,19 +121,6 @@ export type AuthModule = {
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 = {
@ -133,14 +136,14 @@ export type DatabaseModule = {
} & DatabaseStatics;
export type DatabaseModifier = {
id: string;
type: 'orderBy' | 'limit' | 'filter';
name?: string;
key?: string;
limit?: number;
value?: any;
valueType?: string;
}
id: string,
type: 'orderBy' | 'limit' | 'filter',
name?: string,
key?: string,
limit?: number,
value?: any,
valueType?: string,
};
/* Fabric types */
export type CrashlyticsModule = {
@ -150,7 +153,7 @@ export type CrashlyticsModule = {
export type FabricModule = {
crashlytics: CrashlyticsModule,
}
};
/* Firestore types */
@ -164,30 +167,41 @@ export type FirestoreNativeDocumentChange = {
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',
type:
| 'array'
| 'boolean'
| 'date'
| 'documentid'
| 'fieldvalue'
| 'geopoint'
| 'null'
| 'number'
| 'object'
| 'reference'
| 'string',
value: any,
}
};
export type FirestoreWriteOptions = {
merge?: boolean,
}
};
/* Links types */

View File

@ -29,7 +29,10 @@ export default class ModuleBase {
// check if native module exists as all native
initialiseNativeModule(this, config);
initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
initialiseLogger(
this,
`${app.name}:${moduleName.replace('RNFirebase', '')}`
);
}
/**

View File

@ -5,7 +5,14 @@ export default class ReferenceBase {
path: string;
constructor(path: string) {
this.path = path || '/';
if (path) {
this.path =
path.length > 1 && path.endsWith('/')
? path.substring(0, path.length - 1)
: path;
} else {
this.path = '/';
}
}
/**
@ -15,6 +22,8 @@ export default class ReferenceBase {
* {@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);
return this.path === '/'
? null
: this.path.substring(this.path.lastIndexOf('/') + 1);
}
}

View File

@ -8,7 +8,7 @@ import DatabaseSnapshot from '../modules/database/snapshot';
import DatabaseReference from '../modules/database/reference';
import { isString, nativeToJSError } from '../utils';
type Listener = (DatabaseSnapshot) => any;
type Listener = DatabaseSnapshot => any;
type Registration = {
key: string,
@ -19,7 +19,7 @@ type Registration = {
listener: Listener,
eventRegistrationKey: string,
ref: DatabaseReference,
}
};
/**
* Internally used to manage firebase database realtime event
@ -28,16 +28,18 @@ type Registration = {
class SyncTree {
_nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: { [string]: Listener }}};
_tree: { [string]: { [string]: { [string]: Listener } } };
constructor() {
this._tree = {};
this._reverseLookup = {};
if (NativeModules.RNFirebaseDatabase) {
this._nativeEmitter = new NativeEventEmitter(NativeModules.RNFirebaseDatabase);
this._nativeEmitter = new NativeEventEmitter(
NativeModules.RNFirebaseDatabase
);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
this._handleSyncEvent.bind(this)
);
}
}
@ -82,11 +84,10 @@ class SyncTree {
return SharedEventEmitter.emit(
eventRegistrationKey,
new DatabaseSnapshot(registration.ref, snapshot),
previousChildName,
previousChildName
);
}
/**
* Routes native database query listener cancellation events to their js counterparts.
*
@ -96,7 +97,10 @@ class SyncTree {
_handleErrorEvent(event) {
// console.log('SyncTree.ERROR >>>', event);
const { code, message } = event.error;
const { eventRegistrationKey, registrationCancellationKey } = event.registration;
const {
eventRegistrationKey,
registrationCancellationKey,
} = event.registration;
const registration = this.getRegistration(registrationCancellationKey);
@ -121,7 +125,9 @@ class SyncTree {
* @return {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;
}
/**
@ -159,7 +165,9 @@ class SyncTree {
for (let i = 0, len = registrations.length; i < len; i++) {
const registration = registrations[i];
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(
registration
);
if (subscriptions) {
for (let j = 0, l = subscriptions.length; j < l; j++) {
const subscription = subscriptions[j];
@ -188,7 +196,10 @@ class SyncTree {
const eventKeys = Object.keys(this._tree[path] || {});
for (let i = 0, len = eventKeys.length; i < len; i++) {
Array.prototype.push.apply(out, Object.keys(this._tree[path][eventKeys[i]]));
Array.prototype.push.apply(
out,
Object.keys(this._tree[path][eventKeys[i]])
);
}
return out;
@ -216,11 +227,17 @@ class SyncTree {
* @param listener
* @return {Array}
*/
getOneByPathEventListener(path: string, eventType: string, listener: Function): ?string {
getOneByPathEventListener(
path: string,
eventType: string,
listener: Function
): ?string {
if (!this._tree[path]) return null;
if (!this._tree[path][eventType]) return null;
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
const registrationsForPathEvent = Object.entries(
this._tree[path][eventType]
);
for (let i = 0; i < registrationsForPathEvent.length; i++) {
const registration = registrationsForPathEvent[i];
@ -230,7 +247,6 @@ class SyncTree {
return null;
}
/**
* Register a new listener.
*
@ -256,7 +272,7 @@ class SyncTree {
if (once) {
SharedEventEmitter.once(
eventRegistrationKey,
this._onOnceRemoveRegistration(eventRegistrationKey, listener),
this._onOnceRemoveRegistration(eventRegistrationKey, listener)
);
} else {
SharedEventEmitter.addListener(eventRegistrationKey, listener);

View File

@ -18,7 +18,7 @@ import type {
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule }} = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule } } = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
export default {
@ -43,15 +43,23 @@ export default {
* @return {function()}
* @private
*/
appModule<M: FirebaseModule>(app: App, namespace: FirebaseNamespace, InstanceClass: Class<M>): () => FirebaseModule {
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) {
if (
isAndroid &&
namespace !== 'utils' &&
!INTERNALS.FLAGS.checkedPlayServices
) {
INTERNALS.FLAGS.checkedPlayServices = true;
this.utils().checkPlayServicesAvailability();
app.utils().checkPlayServicesAvailability();
}
if (!APP_MODULES[app][namespace]) {
@ -148,16 +156,21 @@ export default {
* @param InstanceClass
* @return {function(App=)}
*/
moduleAndStatics<M: FirebaseModule, S: FirebaseStatics>(namespace: FirebaseNamespace, statics: S, moduleName: FirebaseModuleName): FirebaseModuleAndStatics<M, S> {
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 (_app && !(_app instanceof App))
throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
else if (!_app)
// default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
_app = this.app(DEFAULT_APP_NAME);
if (namespace === 'crashlytics') {
return _app.fabric[namespace]();
}

View File

@ -12,11 +12,15 @@ const NATIVE_SUBSCRIPTIONS: { [string]: boolean } = {};
export const SharedEventEmitter = new EventEmitter();
export const getAppEventName = (module: ModuleBase, eventName: string): string => {
return `${module.app.name}-${eventName}`;
};
export const getAppEventName = (
module: ModuleBase,
eventName: string
): string => `${module.app.name}-${eventName}`;
const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): NativeEventEmitter => {
const getNativeEmitter = (
moduleName: FirebaseModuleName,
module: ModuleBase
): NativeEventEmitter => {
const name = `${module.app.name}-${moduleName}`;
const nativeModule = NativeModules[moduleName];
if (!NATIVE_EMITTERS[name]) {
@ -35,10 +39,14 @@ const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): N
* @param eventName
* @private
*/
const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: ModuleBase, eventName: string): void => {
const subscribeToNativeModuleEvents = (
moduleName: FirebaseModuleName,
module: ModuleBase,
eventName: string
): void => {
if (!NATIVE_SUBSCRIPTIONS[eventName]) {
const nativeEmitter = getNativeEmitter(moduleName, module);
nativeEmitter.addListener(eventName, (event) => {
nativeEmitter.addListener(eventName, event => {
if (event.appName) {
// native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
@ -52,7 +60,10 @@ const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: M
}
};
export const initialiseNativeModuleEventEmitter = (module: ModuleBase, config: FirebaseModuleConfig): void => {
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++) {

View File

@ -4,8 +4,10 @@ import { Platform } from 'react-native';
// todo cleanup unused utilities from legacy code
// modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const PUSH_CHARS =
'-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50;
@ -18,7 +20,11 @@ const { hasOwnProperty } = Object;
* @param joiner
* @returns {*}
*/
export function deepGet(object: Object, path: string, joiner?: string = '/'): any {
export function deepGet(
object: Object,
path: string,
joiner?: string = '/'
): any {
const keys = path.split(joiner);
let i = 0;
@ -42,7 +48,11 @@ export function deepGet(object: Object, path: string, joiner?: string = '/'): an
* @param joiner
* @returns {*}
*/
export function deepExists(object: Object, path: string, joiner?: string = '/'): boolean {
export function deepExists(
object: Object,
path: string,
joiner?: string = '/'
): boolean {
const keys = path.split(joiner);
let i = 0;
@ -64,18 +74,23 @@ export function deepExists(object: Object, path: string, joiner?: string = '/'):
* @param obj2
* @returns {boolean}
*/
export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): boolean {
export function areObjectKeysContainedInOther(
obj1: Object,
obj2: Object
): boolean {
if (!isObject(obj1) || !isObject(obj2)) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (isArrayContainedInOther(keys1, keys2)) {
return keys1.filter((key) => {
return isObject(obj1[key]);
}).reduce((acc, cur) => {
return acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]);
}, true);
return keys1
.filter(key => isObject(obj1[key]))
.reduce(
(acc, cur) =>
acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]),
true
);
}
return false;
}
@ -86,13 +101,14 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2
* @returns {boolean}
*/
export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean {
export function isArrayContainedInOther(
arr1: Array<*>,
arr2: Array<*>
): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false;
}
return arr1.reduce((acc, cur) => {
return acc && arr2.includes(cur);
}, true);
return arr1.reduce((acc, cur) => acc && arr2.includes(cur), true);
}
/**
@ -101,7 +117,9 @@ export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean
* @returns {boolean}
*/
export function isObject(item: mixed): boolean %checks {
return item ? (typeof item === 'object' && !Array.isArray(item) && item !== null) : false;
return item
? typeof item === 'object' && !Array.isArray(item) && item !== null
: false;
}
/**
@ -126,7 +144,6 @@ export function isString(value: mixed): boolean %checks {
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
/**
*
* @param string
@ -153,16 +170,16 @@ export function tryJSONStringify(data: mixed): string | null {
}
}
// noinspection Eslint
export const windowOrGlobal = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this;
export const windowOrGlobal =
// eslint-disable-next-line no-restricted-globals
(typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this;
/**
* No operation func
*/
export function noop(): void {
}
export function noop(): void {}
// /**
// * Delays chunks based on sizes per event loop.
@ -286,7 +303,7 @@ const lastRandChars = [];
export function generatePushID(serverTimeOffset?: number = 0): string {
const timeStampChars = new Array(8);
let now = new Date().getTime() + serverTimeOffset;
const duplicateTime = (now === lastPushTime);
const duplicateTime = now === lastPushTime;
lastPushTime = now;
@ -295,7 +312,8 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
now = Math.floor(now / 64);
}
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
if (now !== 0)
throw new Error('We should have converted the entire timestamp.');
let id = timeStampChars.join('');
@ -330,7 +348,11 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
* @param additionalProps
* @returns {Error}
*/
export function nativeToJSError(code: string, message: string, additionalProps?: Object = {}) {
export function nativeToJSError(
code: string,
message: string,
additionalProps?: Object = {}
) {
const error: Object = new Error(message);
error.code = code;
Object.assign(error, additionalProps);
@ -362,7 +384,6 @@ export function objectToUniqueId(object: Object): string {
return key;
}
/**
* Return the existing promise if no callback provided or
* exec the promise and callback if optionalCallback is valid.
@ -371,23 +392,28 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback
* @return {Promise}
*/
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> {
export function promiseOrCallback(
promise: Promise<*>,
optionalCallback?: Function
): Promise<*> {
if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else if (optionalCallback) {
optionalCallback(null, result);
}
return promise
.then(result => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else if (optionalCallback) {
optionalCallback(null, result);
}
return Promise.resolve(result);
}).catch((error) => {
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
return Promise.resolve(result);
})
.catch(error => {
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
}
/**
@ -398,7 +424,9 @@ export function firestoreAutoId(): string {
let autoId = '';
for (let i = 0; i < 20; i++) {
autoId += AUTO_ID_CHARS.charAt(Math.floor(Math.random() * AUTO_ID_CHARS.length));
autoId += AUTO_ID_CHARS.charAt(
Math.floor(Math.random() * AUTO_ID_CHARS.length)
);
}
return autoId;
}

View File

@ -29,17 +29,20 @@ const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.',
message:
'The installed version of Google Play services on this device is out of date.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
3: {
code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.',
message:
'The installed version of Google Play services has been disabled on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
9: {
code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.',
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: {
@ -49,7 +52,8 @@ const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
19: {
code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.',
message:
"Google Play service doesn't have one or more required permissions.",
},
};
@ -66,27 +70,33 @@ export default {
},
STRINGS: {
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' +
'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.',
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.",
/**
* @return {string}
*/
get ERROR_MISSING_CORE() {
if (Platform.OS === 'ios') {
return 'RNFirebase core module was not found natively on iOS, ensure you have ' +
return (
'RNFirebase core module was not found natively on iOS, ensure you have ' +
'correctly included the RNFirebase pod in your projects `Podfile` and have run `pod install`.' +
'\r\n\r\n See http://invertase.link/ios for the ios setup guide.';
'\r\n\r\n See http://invertase.link/ios for the ios setup guide.'
);
}
return 'RNFirebase core module was not found natively on Android, ensure you have ' +
return (
'RNFirebase core module was not found natively on Android, ensure you have ' +
'correctly added the RNFirebase and Firebase gradle dependencies to your `android/app/build.gradle` file.' +
'\r\n\r\n See http://invertase.link/android for the android setup guide.';
'\r\n\r\n See http://invertase.link/android for the android setup guide.'
);
},
ERROR_INIT_OBJECT: 'Firebase.initializeApp(options <-- requires a valid configuration object.',
ERROR_INIT_STRING_NAME: 'Firebase.initializeApp(options, name <-- requires a valid string value.',
ERROR_INIT_OBJECT:
'Firebase.initializeApp(options <-- requires a valid configuration object.',
ERROR_INIT_STRING_NAME:
'Firebase.initializeApp(options, name <-- requires a valid string value.',
/**
* @return {string}
@ -131,20 +141,28 @@ export default {
ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
const snippet = `firebase.${namespace}()`;
if (Platform.OS === 'ios') {
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
return (
`You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
'\r\n\r\nEnsure you have the required Firebase iOS SDK pod for this module included in your Podfile, in this instance ' +
`confirm you've added "pod '${NAMESPACE_PODS[namespace]}'" to your Podfile` +
'\r\n\r\nSee http://invertase.link/ios for full setup instructions.';
`confirm you've added "pod '${
NAMESPACE_PODS[namespace]
}'" to your Podfile` +
'\r\n\r\nSee http://invertase.link/ios for full setup instructions.'
);
}
const fbSDKDep = `'com.google.firebase:firebase-${GRADLE_DEPS[namespace] || namespace}'`;
const fbSDKDep = `'com.google.firebase:firebase-${GRADLE_DEPS[
namespace
] || namespace}'`;
const rnFirebasePackage = `'io.invertase.firebase.${namespace}.${nativeModule}Package'`;
const newInstance = `'new ${nativeModule}Package()'`;
return `You attempted to use a firebase module that's not installed on your Android project by calling ${snippet}.` +
return (
`You attempted to use a firebase module that's not installed on your Android project by calling ${snippet}.` +
`\r\n\r\nEnsure you have:\r\n\r\n1) Installed the required Firebase Android SDK dependency ${fbSDKDep} in your 'android/app/build.gradle' ` +
`file.\r\n\r\n2) Imported the ${rnFirebasePackage} module in your 'MainApplication.java' file.\r\n\r\n3) Added the ` +
`${newInstance} line inside of the RN 'getPackages()' method list.` +
'\r\n\r\nSee http://invertase.link/android for full setup instructions.';
'\r\n\r\nSee http://invertase.link/android for full setup instructions.'
);
},
/**
@ -191,29 +209,30 @@ export default {
return `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
},
/**
* @return {string}
*/
ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode];
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
let start =
'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
if (statusCode === 2) {
start = 'Google Play Services is out of date and may cause some firebase services like authentication to hang when used. It is recommended that you update it.';
start =
'Google Play Services is out of date and may cause some firebase services like authentication to hang when used. It is recommended that you update it.';
}
// eslint-disable-next-line prefer-template
return `${start}\r\n\r\n` +
'-------------------------\r\n' +
(knownError ?
`${knownError.code}: ${knownError.message} (code ${statusCode})` :
`A specific play store availability reason reason was not available (unknown code: ${statusCode})`
) +
'\r\n-------------------------' +
'\r\n\r\n' +
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' +
'\r\n\r\nhttp://invertase.link/play-services';
return (
`${`${start}\r\n\r\n-------------------------\r\n`}${
knownError
? `${knownError.code}: ${knownError.message} (code ${statusCode})`
: `A specific play store availability reason reason was not available (unknown code: ${statusCode})`
}\r\n-------------------------` +
`\r\n\r\n` +
`For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:` +
`\r\n\r\nhttp://invertase.link/play-services`
);
},
},
};

View File

@ -5,7 +5,7 @@ import { windowOrGlobal } from './';
import type ModuleBase from './ModuleBase';
((base) => {
(base => {
window = base || window;
// $FlowFixMe: Why are we using localStorage at all?
if (!window.localStorage) window.localStorage = {};
@ -15,7 +15,8 @@ import type ModuleBase from './ModuleBase';
const NATIVE_LOGGERS: { [string]: Object } = {};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
const getModuleKey = (module: ModuleBase): string =>
`${module.app.name}:${module.namespace}`;
export const getLogger = (module: ModuleBase) => {
const key = getModuleKey(module);
@ -25,16 +26,18 @@ export const getLogger = (module: ModuleBase) => {
export const initialiseLogger = (module: ModuleBase, logNamespace: string) => {
const key = getModuleKey(module);
if (!NATIVE_LOGGERS[key]) {
// eslint-disable-next-line global-require
NATIVE_LOGGERS[key] = require('bows')(`🔥 ${logNamespace.toUpperCase()}`);
}
};
export default class Log {
static createLogger(namespace) {
static createLogger(namespace: string) {
// eslint-disable-next-line global-require
return require('bows')(namespace);
}
static setLevel(booleanOrDebugString) {
static setLevel(booleanOrDebugString: boolean | string) {
window.localStorage.debug = booleanOrDebugString;
window.localStorage.debugColors = !!booleanOrDebugString;
}

View File

@ -8,14 +8,6 @@ import INTERNALS from './internals';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig } from '../types';
// Firebase Native SDKs that support multiple app instances
const MULTI_APP_MODULES = [
'RNFirebaseAuth',
'RNFirebaseDatabase',
'RNFirebaseFirestore',
'RNFirebaseStorage',
];
const NATIVE_MODULES: { [string]: Object } = {};
/**
@ -29,36 +21,40 @@ const nativeWithApp = (appName: string, NativeModule: Object): Object => {
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
native[method] = (...args) => NativeModule[method](...[appName, ...args]);
}
return native;
};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
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, namespace } = config;
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));
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_MODULE(namespace, moduleName)
);
}
// used by the modules that extend ModuleBase
// to access their native module counterpart
if (!MULTI_APP_MODULES.includes(moduleName)) {
NATIVE_MODULES[key] = nativeModule;
} else {
if (multiApp) {
NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule);
} else {
NATIVE_MODULES[key] = nativeModule;
}
initialiseNativeModuleEventEmitter(module, config);

947
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,29 @@
{
"name": "react-native-firebase",
"version": "3.2.0",
"version": "3.2.4",
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Messaging (FCM), Remote Config, Storage and Performance.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "npm run build-lib && npm run build-flow",
"build": "genversion lib/version.js && npm run validate-ts-declarations && npm run build-lib && npm run build-flow",
"build-flow": "flow-copy-source -i */__tests__* lib dist",
"build-lib": "BABEL_ENV=publish babel lib -d dist --ignore __tests__ --copy-files",
"clean": "rimraf dist/",
"flow": "flow",
"lint": "eslint ./src",
"format:assets": "prettier --ignore-path .gitignore --write \"**/*.{json,md}\"",
"format": "npm run format:assets && npm run lint -- --fix",
"lint": "eslint ./lib ./tests/src ./tests/lib",
"precommit": "lint-staged",
"prepublish": "npm run clean && npm run build",
"postinstall": "postinstall-build dist && opencollective postinstall",
"postinstall": "postinstall-build dist && opencollective postinstall || exit 0",
"test-cli": "node ./bin/test.js",
"tests-packager": "cd tests && npm run start",
"tests-npm-install": "cd tests && npm install",
"tests-pod-install": "cd tests && npm run ios:pod:install",
"tests-watch-start": "npm run test-cli watch init start",
"tests-watch-stop": "npm run test-cli watch stop"
"tests-watch-stop": "npm run test-cli watch stop",
"validate-ts-declarations": "tsc --project ./"
},
"repository": {
"type": "git",
@ -28,45 +32,10 @@
"jest": {
"preset": "jest-react-native",
"setupFiles": [],
"unmockedModulePathPatterns": [
"./node_modules/react",
"./node_modules/react-native",
"./node_modules/react-native-mock",
"./node_modules/react-addons-test-utils"
]
"unmockedModulePathPatterns": ["./node_modules/react", "./node_modules/react-native", "./node_modules/react-native-mock", "./node_modules/react-addons-test-utils"]
},
"license": "APACHE-2.0",
"keywords": [
"react",
"admob",
"auth",
"config",
"digits",
"phone-auth",
"sms",
"firestore",
"cloud-firestore",
"datastore",
"remote-config",
"transactions",
"react-native",
"react-native-firebase",
"firebase",
"fcm",
"apn",
"gcm",
"analytics",
"messaging",
"database",
"android",
"ios",
"crash",
"firestack",
"performance",
"firestore",
"dynamic-links",
"crashlytics"
],
"keywords": ["react", "admob", "auth", "config", "digits", "phone-auth", "sms", "firestore", "cloud-firestore", "datastore", "remote-config", "transactions", "react-native", "react-native-firebase", "firebase", "fcm", "apn", "gcm", "analytics", "messaging", "database", "android", "ios", "crash", "firestack", "performance", "firestore", "dynamic-links", "crashlytics"],
"peerDependencies": {
"react": "*",
"react-native": ">= 0.48.0",
@ -82,17 +51,24 @@
"enzyme": "^2.4.1",
"eslint": "^4.11.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-flowtype": "^2.39.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.4.0",
"flow-bin": "^0.56.0",
"flow-bin": "^0.61.0",
"flow-copy-source": "^1.2.1",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-native": "^0.50.3",
"genversion": "^2.0.1",
"husky": "^0.14.3",
"lint-staged": "^6.0.1",
"prettier": "1.10.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-native": "^0.52.0",
"rimraf": "^2.6.2",
"shelljs": "^0.7.8",
"typescript": "^2.6.2",
"wml": "0.0.82"
},
"dependencies": {
@ -115,5 +91,10 @@
"type": "opencollective",
"url": "https://opencollective.com/react-native-firebase",
"logo": "https://opencollective.com/opencollective/logo.txt"
},
"lint-staged": {
"lib/**/*.js": ["eslint --fix", "git add"],
"tests/{src|lib}/**/*.js": ["eslint --fix", "git add"],
"*.{json,md,scss}": ["prettier --write", "git add"]
}
}

12
prettier.config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
trailingComma: 'es5',
singleQuote: true,
overrides: [
{
files: '*.json',
options: {
printWidth: 400,
},
},
],
};

View File

@ -1,42 +1,39 @@
{
"extends": "airbnb",
"extends": [
"airbnb",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"flowtype"
"flowtype",
"prettier"
],
"env": {
"es6": true,
"jasmine": true
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": {
"no-plusplus": 0,
"prettier/prettier": ["error", {
"trailingComma": "es5",
"singleQuote": true
}],
"react/forbid-prop-types": "warn",
"react/jsx-filename-extension": [
"off", { "extensions": [".js", ".jsx"] }
],
"class-methods-use-this": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"arrow-body-style": 0,
"import/prefer-default-export": 0,
"radix": 0,
"new-cap": 0,
"max-len": 0,
"no-continue": 0,
"no-console": 0,
"global-require": 0,
"import/extensions": 0,
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0
"no-plusplus": 0,
"no-undef": 0,
"no-underscore-dangle": "off",
"no-use-before-define": 0
},
"globals": {
"__DEV__": true,
"window": true,
"fetch": true,
"window": true
}
}

View File

@ -12,36 +12,45 @@
; 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
.*/Libraries/react-native/ReactNative.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
flow/
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
module.system=haste
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^~\/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-8]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.38.0
^0.61.0

View File

@ -1,5 +1,3 @@
import re
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
@ -11,8 +9,9 @@ import re
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
@ -20,7 +19,7 @@ for jarfile in glob(['libs/*.jar']):
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
@ -28,39 +27,39 @@ for aarfile in glob(['libs/*.aar']):
)
android_library(
name = 'all-libs',
exported_deps = lib_deps
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = 'app-code',
srcs = glob([
'src/main/java/**/*.java',
]),
deps = [
':all-libs',
':build_config',
':res',
],
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = 'build_config',
package = 'com.reactnativefirebasedemo',
name = "build_config",
package = "com.react-native-firebase-tests",
)
android_resource(
name = 'res',
res = 'src/main/res',
package = 'com.reactnativefirebasedemo',
name = "res",
package = "com.react-native-firebase-tests",
res = "src/main/res",
)
android_binary(
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores:debug',
deps = [
':app-code',
],
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View File

@ -4,11 +4,78 @@ apply plugin: 'io.fabric'
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
// whether to bundle JS and assets in staging mode
bundleInDebug : false,
jsBundleDirDebug : "$buildDir/intermediates/assets/debug",
nodeExecutableAndArgs: ["/usr/local/bin/node"]
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
@ -63,7 +130,7 @@ android {
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a": 1, "x86": 2]
def versionCodes = ["armeabi-v7a":1, "x86":2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
@ -73,14 +140,13 @@ android {
}
}
project.ext.firebaseVersion = '11.6.2'
project.ext.firebaseVersion = '11.8.0'
dependencies {
// compile(project(':react-native-firebase')) {
// transitive = false
// }
compile project(':react-native-vector-icons')
compile project(':react-native-firebase')
compile(project(':react-native-firebase')) {
transitive = false
}
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.google.android.gms:play-services-base:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion"

View File

@ -50,6 +50,10 @@
-dontwarn com.facebook.react.**
# TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details.
-dontwarn android.text.StaticLayout
# okhttp
-keepattributes Signature

View File

@ -53,6 +53,11 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseStoragePackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override

View File

@ -1,4 +1,3 @@
#Tue Mar 07 13:10:12 GMT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

10
tests/android/gradlew vendored
View File

@ -42,6 +42,11 @@ case "`uname`" in
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -56,9 +61,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -109,7 +114,6 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`

View File

@ -1,8 +1,8 @@
keystore(
name = 'debug',
store = 'debug.keystore',
properties = 'debug.keystore.properties',
visibility = [
'PUBLIC',
],
name = "debug",
properties = "debug.keystore.properties",
store = "debug.keystore",
visibility = [
"PUBLIC",
],
)

View File

@ -1,5 +0,0 @@
import { AppRegistry } from 'react-native';
import bootstrap from './src/main';
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);

View File

@ -1,5 +1,4 @@
import { AppRegistry } from 'react-native';
import bootstrap from './src/main';
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);

View File

@ -40,6 +40,12 @@ target 'ReactNativeFirebaseDemo' do
if target.name == "React"
target.remove_from_project
end
if target.name == 'yoga'
target.build_configurations.each do |config|
config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'
config.build_settings['GCC_WARN_64_TO_32_BIT_CONVERSION'] = 'NO'
end
end
end
end
end

View File

@ -1,231 +0,0 @@
PODS:
- BoringSSL (9.1):
- BoringSSL/Implementation (= 9.1)
- BoringSSL/Interface (= 9.1)
- BoringSSL/Implementation (9.1):
- BoringSSL/Interface (= 9.1)
- BoringSSL/Interface (9.1)
- Crashlytics (3.9.3):
- Fabric (~> 1.7.2)
- Fabric (1.7.2)
- Firebase/AdMob (4.5.0):
- Firebase/Core
- Google-Mobile-Ads-SDK (= 7.25.0)
- Firebase/Auth (4.5.0):
- Firebase/Core
- FirebaseAuth (= 4.3.1)
- Firebase/Core (4.5.0):
- FirebaseAnalytics (= 4.0.4)
- FirebaseCore (= 4.0.10)
- Firebase/Crash (4.5.0):
- Firebase/Core
- FirebaseCrash (= 2.0.2)
- Firebase/Database (4.5.0):
- Firebase/Core
- FirebaseDatabase (= 4.1.0)
- Firebase/DynamicLinks (4.5.0):
- Firebase/Core
- FirebaseDynamicLinks (= 2.1.0)
- Firebase/Firestore (4.5.0):
- Firebase/Core
- FirebaseFirestore (= 0.9.1)
- Firebase/Messaging (4.5.0):
- Firebase/Core
- FirebaseMessaging (= 2.0.6)
- Firebase/Performance (4.5.0):
- Firebase/Core
- FirebasePerformance (= 1.0.6)
- Firebase/RemoteConfig (4.5.0):
- Firebase/Core
- FirebaseRemoteConfig (= 2.1.0)
- Firebase/Storage (4.5.0):
- Firebase/Core
- FirebaseStorage (= 2.0.2)
- FirebaseABTesting (1.0.0):
- FirebaseCore (~> 4.0)
- Protobuf (~> 3.1)
- FirebaseAnalytics (4.0.4):
- FirebaseCore (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- nanopb (~> 0.3)
- FirebaseAuth (4.3.1):
- FirebaseAnalytics (~> 4.0)
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (4.0.10):
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- nanopb (~> 0.3)
- FirebaseCrash (2.0.2):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- Protobuf (~> 3.1)
- FirebaseDatabase (4.1.0):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- leveldb-library (~> 1.18)
- FirebaseDynamicLinks (2.1.0):
- FirebaseAnalytics (~> 4.0)
- FirebaseFirestore (0.9.1):
- FirebaseAnalytics (~> 4.0)
- FirebaseAuth (~> 4.3)
- FirebaseCore (~> 4.0)
- gRPC-ProtoRPC (~> 1.0)
- leveldb-library (~> 1.18)
- Protobuf (~> 3.1)
- FirebaseInstanceID (2.0.5)
- FirebaseMessaging (2.0.6):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- Protobuf (~> 3.1)
- FirebasePerformance (1.0.6):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- GTMSessionFetcher/Core (~> 1.1)
- Protobuf (~> 3.1)
- FirebaseRemoteConfig (2.1.0):
- FirebaseABTesting (~> 1.0)
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- Protobuf (~> 3.1)
- FirebaseStorage (2.0.2):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- GTMSessionFetcher/Core (~> 1.1)
- Google-Mobile-Ads-SDK (7.25.0)
- GoogleToolboxForMac/DebugUtils (2.1.3):
- GoogleToolboxForMac/Defines (= 2.1.3)
- GoogleToolboxForMac/Defines (2.1.3)
- GoogleToolboxForMac/Logger (2.1.3):
- GoogleToolboxForMac/Defines (= 2.1.3)
- GoogleToolboxForMac/NSData+zlib (2.1.3):
- GoogleToolboxForMac/Defines (= 2.1.3)
- GoogleToolboxForMac/NSDictionary+URLArguments (2.1.3):
- GoogleToolboxForMac/DebugUtils (= 2.1.3)
- GoogleToolboxForMac/Defines (= 2.1.3)
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.3)
- GoogleToolboxForMac/NSString+URLArguments (2.1.3)
- gRPC (1.7.0):
- gRPC-RxLibrary (= 1.7.0)
- gRPC/Main (= 1.7.0)
- gRPC-Core (1.7.0):
- gRPC-Core/Implementation (= 1.7.0)
- gRPC-Core/Interface (= 1.7.0)
- gRPC-Core/Implementation (1.7.0):
- BoringSSL (~> 9.0)
- gRPC-Core/Interface (= 1.7.0)
- nanopb (~> 0.3)
- gRPC-Core/Interface (1.7.0)
- gRPC-ProtoRPC (1.7.0):
- gRPC (= 1.7.0)
- gRPC-RxLibrary (= 1.7.0)
- Protobuf (~> 3.0)
- gRPC-RxLibrary (1.7.0)
- gRPC/Main (1.7.0):
- gRPC-Core (= 1.7.0)
- gRPC-RxLibrary (= 1.7.0)
- GTMSessionFetcher/Core (1.1.12)
- leveldb-library (1.20)
- nanopb (0.3.8):
- nanopb/decode (= 0.3.8)
- nanopb/encode (= 0.3.8)
- nanopb/decode (0.3.8)
- nanopb/encode (0.3.8)
- Protobuf (3.4.0)
- React (0.49.1):
- React/Core (= 0.49.1)
- React/BatchedBridge (0.49.1):
- React/Core
- React/cxxreact_legacy
- React/Core (0.49.1):
- yoga (= 0.49.1.React)
- React/cxxreact_legacy (0.49.1):
- React/jschelpers_legacy
- React/fishhook (0.49.1)
- React/jschelpers_legacy (0.49.1)
- React/RCTBlob (0.49.1):
- React/Core
- React/RCTNetwork (0.49.1):
- React/Core
- React/RCTText (0.49.1):
- React/Core
- React/RCTWebSocket (0.49.1):
- React/Core
- React/fishhook
- React/RCTBlob
- RNFirebase (3.2.0):
- React
- yoga (0.49.1.React)
DEPENDENCIES:
- Crashlytics (~> 3.9.3)
- Fabric (~> 1.7.2)
- Firebase/AdMob
- Firebase/Auth
- Firebase/Core
- Firebase/Crash
- Firebase/Database
- Firebase/DynamicLinks
- Firebase/Firestore
- Firebase/Messaging
- Firebase/Performance
- Firebase/RemoteConfig
- Firebase/Storage
- React/BatchedBridge (from `../node_modules/react-native`)
- React/Core (from `../node_modules/react-native`)
- React/RCTNetwork (from `../node_modules/react-native`)
- React/RCTText (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- RNFirebase (from `../../ios/RNFirebase.podspec`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
EXTERNAL SOURCES:
React:
:path: ../node_modules/react-native
RNFirebase:
:path: ../../ios/RNFirebase.podspec
yoga:
:path: ../node_modules/react-native/ReactCommon/yoga
SPEC CHECKSUMS:
BoringSSL: 84318770d120503ab1a6aaf1df777c5ca053697e
Crashlytics: dbb07d01876c171c5ccbdf7826410380189e452c
Fabric: 9cd6a848efcf1b8b07497e0b6a2e7d336353ba15
Firebase: d83d287bdb6b1c78320561482706d1fcfc8ddfc1
FirebaseABTesting: d07d0ee833b842d5153549e4c7e2e2cb1c23a3f9
FirebaseAnalytics: 722b53c7b32bfc7806b06e0093a2f5180d4f2c5a
FirebaseAuth: e7c2b1359c2df4af23a568100a8eace0b8c507e4
FirebaseCore: da5e8e372cb6046212739a4d81e42d32c22907cc
FirebaseCrash: cded0fc566c03651aea606a101bc156085f333ca
FirebaseDatabase: 607284a103e961d7f5863ee603cab5e85f443bd6
FirebaseDynamicLinks: ed4cb6c42705aaa5e841ed2d76e3a4bddbec10c1
FirebaseFirestore: 2112e800ec742daa37b3500b227aaa13e7ca236c
FirebaseInstanceID: f2b688c66b972f30d7fa9f5f9f91455454a03b47
FirebaseMessaging: 75d83375b0f7a9911568d7bdd1a1a8261cbcbdef
FirebasePerformance: fa032c27e229eb8c1a8638918793fe2e47465205
FirebaseRemoteConfig: 451fe8e9c43ac1e7a137ad2a42189bfc8c2c3ebc
FirebaseStorage: 0cca42d9b889a0227c3a50121f45a4469fc9eb27
Google-Mobile-Ads-SDK: 113804f266381a30f982ad3c18063d4e0155414f
GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20
gRPC: 4322828abb82ceee683b8205c352143e15ba4cef
gRPC-Core: 156780506d5e9b1b4487a108e8e8eb5bd966da22
gRPC-ProtoRPC: cb11e31dba9a17fed05988c59504c6db77838746
gRPC-RxLibrary: d13658486ddda3d31a460c17a5f012a6797b608f
GTMSessionFetcher: ebaa1f79a5366922c1735f1566901f50beba23b7
leveldb-library: 9e29a1d913f922444127a7d0f28b025a0e2ba053
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
RNFirebase: 22b1917fec663706907bc901ed665ac4f8b9bfd6
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
PODFILE CHECKSUM: f17a538903249834df5049668d10174810db4c4c
COCOAPODS: 1.3.1

View File

@ -18,14 +18,14 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FIRApp configure];
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactNativeFirebaseDemo"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

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