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", "parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [ "plugins": [
"flowtype" "flowtype",
"prettier"
], ],
"env": { "env": {
"es6": true, "es6": true,
"jasmine": true "jasmine": true
}, },
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": { "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, "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, "no-console": 0,
"global-require": 0, "no-plusplus": 0,
"import/extensions": 0, "no-undef": 0,
"import/no-unresolved": 0, "no-underscore-dangle": "off",
"import/no-extraneous-dependencies": 0, "no-use-before-define": 0
"react/jsx-filename-extension": 0,
"no-unused-expressions": 0,
"flowtype/no-unused-expressions": ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}]
}, },
"globals": { "globals": {
"__DEV__": true, "__DEV__": true,

View File

@ -3,7 +3,7 @@
.*/*[.]android.js .*/*[.]android.js
; Ignore "BUCK" generated dirs ; Ignore "BUCK" generated dirs
.*/node_modules/react-native/\.buckd/ <PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule" ; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.* .*/node_modules/.*/node_modules/fbjs/.*
@ -16,22 +16,8 @@
; Ignore polyfills ; Ignore polyfills
.*/Libraries/polyfills/.* .*/Libraries/polyfills/.*
# React Native problems ; Ignore metro
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaCalculator.js.flow .*/node_modules/metro/.*
.*/node_modules/metro-bundler/src/DeltaBundler/DeltaPatcher.js.flow
.*/node_modules/metro-bundler/src/node-haste/AssetResolutionCache.js.flow
.*/node_modules/metro-bundler/src/node-haste/DependencyGraph.js.flow
#.*/node_modules/react-native/Libraries/Animated/src/nodes/AnimatedStyle.js
#.*/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableFlatList.js
#.*/node_modules/react-native/Libraries/Experimental/SwipeableRow/SwipeableListView.js
#.*/node_modules/react-native/Libraries/Image/ImageBackground.js
#.*/node_modules/react-native/Libraries/Lists/FlatList.js
#.*/node_modules/react-native/Libraries/Lists/MetroListView.js
#.*/node_modules/react-native/Libraries/Lists/SectionList.js
#.*/node_modules/react-native/Libraries/Lists/ViewabilityHelper.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedList.js
#.*/node_modules/react-native/Libraries/Lists/VirtualizedSectionList.js
# Ignore dist folder # Ignore dist folder
.*/dist/.* .*/dist/.*
@ -44,12 +30,13 @@
[libs] [libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/ node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options] [options]
module.system=haste
emoji=true emoji=true
module.system=haste
munge_underscores=true 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' 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=$FlowFixMe
suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState 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\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(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\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowBug.* suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
@ -74,4 +60,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
unsafe.enable_getters_and_setters=true unsafe.enable_getters_and_setters=true
[version] [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 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. 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). --> <!--- 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 ### Environment
<!--- (e.g. iOS, Android, Both) ---> <!--- (e.g. iOS, Android, Both) --->
1. Application Target Platform:
1. Application Target Platform:
<!--- (e.g. macOS Sierra, Windows 10) ---> <!--- (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) ---> <!--- (Xcode or Android Studio version, iOS or Android SDK version - if relevant) --->
3. Build Tools:
3. Build Tools:
<!--- (e.g. 0.45.1) ---> <!--- (e.g. 0.45.1) --->
4. React Native version:
4. React Native version:
<!--- (e.g. 2.1.3) ---> <!--- (e.g. 2.1.3) --->
5. RNFirebase Version:
5. RNFirebase Version:
<!--- (e.g. database, auth, messaging, analytics etc - or N/A if not applicable) ---> <!--- (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: <!-- Love react-native-firebase? Please consider supporting our collective:
👉 https://opencollective.com/react-native-firebase/donate --> 👉 https://opencollective.com/react-native-firebase/donate -->

2
.gitignore vendored
View File

@ -10,6 +10,7 @@ npm-debug.log
*.perspectivev3 *.perspectivev3
*.xcuserstate *.xcuserstate
project.xcworkspace/ project.xcworkspace/
atlassian-ide-plugin
xcuserdata/ xcuserdata/
# Example # Example
@ -78,3 +79,4 @@ local.properties
**/ios/Pods/** **/ios/Pods/**
**/ios/ReactNativeFirebaseDemo.xcworkspace/ **/ios/ReactNativeFirebaseDemo.xcworkspace/
dist 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! 😊 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 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, ... * **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... * **Marketing**: writing blog posts, howto's, printing stickers, ...
- **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. * **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-firebase). * **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 ## 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 ## 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! 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> <a href="graphs/contributors"><img src="https://opencollective.com/react-native-firebase/contributors.svg?width=890" /></a>
### Backers ### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/react-native-firebase#backer)] 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> <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 ### 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)) 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="/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="#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="#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> <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> </p>
@ -30,43 +30,44 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
--- ---
## Supported Firebase Features ## 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 > '**?**' indicates partial support
| Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK | | Firebase Features | v1.x.x | v2.x.x | v3.x.x | v3.1.x | v3.2.x | Web SDK |
| ---------------------- | :---: | :---: | :---: | :---: | :---: | :---: | | -------------------------- | :----: | :----: | :----: | :----: | :----: | :-----: |
| **AdMob** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | | **AdMob** | | | | | | |
| **Analytics**             | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | **Analytics**             | | | | | | |
| **App Indexing**           | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **App Indexing**           | | | | | | |
| **Authentication** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **Authentication** | | | | | | |
| _-- Phone Auth_ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | | _-- Phone Auth_ | | | | | | |
| **Core** | ❌ |**?**| ✅ | ✅ | ✅ | ✅ | | **Core** | | **?** | | | | |
| _-- Multiple Apps_ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | | _-- Multiple Apps_ | | | | | | |
| **Cloud Firestore** | ❌ | ❌ | ✅ | ✅ | ✅ |**?**| | **Cloud Firestore** | | | | | | **?** |
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**| | **Cloud Messaging (FCM)** | | | | | | **?** |
| **Crashlytics**           | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | | **Crashlytics**           | | | | | | |
| **Crash Reporting** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | **Crash Reporting** | | | | | | |
| **Dynamic Links** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | | **Dynamic Links** | | | | | | |
| **Invites** | ❌ | ❌ | ❌ |**?**|**?**| ❌ | | **Invites** | | | | **?** | **?** | |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | **Performance Monitoring** | | | | | | |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **Realtime Database** | | | | | | |
| _-- Offline Persistence_ | ✅ | ✅ | ✅ | ✅ | ✅ |**?**| | _-- Offline Persistence_ | | | | | | **?** |
| _-- Transactions_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | _-- Transactions_ | | | | | | |
| **Remote Config** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | **Remote Config** | | | | | | |
| **Storage** | ✅ | ✅ | ✅ | ✅ | ✅ |**?**| | **Storage** | | | | | | **?** |
--- ---
### Supported versions - React Native / Firebase ### Supported versions - React Native / Firebase
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase` > The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X | 3.2.X | | | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X | 3.1.X | 3.2.X |
|------------------------|-------------|-------------|-----------------|----------|-------------|----------| | -------------------- | ----------- | ----------- | ------------- | -------- | ----------- | -------- |
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + | 0.48 - 0.49 | 0.50 + | | 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 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 + | | 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 ## 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 ## 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: 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 ### Sponsors
@ -131,4 +132,4 @@ Thank you to all the people who have already contributed to RNFirebase!
## License ## License
- See [LICENSE](/LICENSE) * See [LICENSE](/LICENSE)

View File

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

View File

@ -6,6 +6,7 @@ import android.util.Log;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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.AuthResult;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseAuthProvider; import com.google.firebase.auth.FirebaseAuthProvider;
import com.google.firebase.auth.FirebaseUserMetadata;
import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GithubAuthProvider;
import com.google.firebase.auth.OAuthProvider;
import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.auth.ProviderQueryResult; import com.google.firebase.auth.ProviderQueryResult;
@ -91,13 +94,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
FirebaseUser user = firebaseAuth.getCurrentUser(); FirebaseUser user = firebaseAuth.getCurrentUser();
WritableMap msgMap = Arguments.createMap(); WritableMap msgMap = Arguments.createMap();
if (user != null) { if (user != null) {
msgMap.putBoolean("authenticated", true);
msgMap.putString("appName", appName); // for js side distribution msgMap.putString("appName", appName); // for js side distribution
msgMap.putMap("user", firebaseUserToMap(user)); msgMap.putMap("user", firebaseUserToMap(user));
Utils.sendEvent(mReactContext, "auth_state_changed", msgMap); Utils.sendEvent(mReactContext, "auth_state_changed", msgMap);
} else { } else {
msgMap.putString("appName", appName); // for js side distribution msgMap.putString("appName", appName); // for js side distribution
msgMap.putBoolean("authenticated", false);
Utils.sendEvent(mReactContext, "auth_state_changed", msgMap); Utils.sendEvent(mReactContext, "auth_state_changed", msgMap);
} }
} }
@ -178,11 +179,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
} }
/**
* signOut
*
* @param promise
*/
@ReactMethod @ReactMethod
public void signOut(String appName, Promise promise) { public void signOut(String appName, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -197,13 +193,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
} }
/**
* signInAnonymously
*
* @param promise
*/
@ReactMethod @ReactMethod
public void signInAnonymously(String appName, final Promise promise) { 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); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -213,7 +213,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override @Override
public void onSuccess(AuthResult authResult) { public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInAnonymously:onComplete:success"); Log.d(TAG, "signInAnonymously:onComplete:success");
promiseWithUser(authResult.getUser(), promise); if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
} }
}) })
.addOnFailureListener(new OnFailureListener() { .addOnFailureListener(new OnFailureListener() {
@ -234,6 +238,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
*/ */
@ReactMethod @ReactMethod
public void createUserWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) { 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"); Log.d(TAG, "createUserWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -243,7 +256,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override @Override
public void onSuccess(AuthResult authResult) { public void onSuccess(AuthResult authResult) {
Log.d(TAG, "createUserWithEmailAndPassword:onComplete:success"); Log.d(TAG, "createUserWithEmailAndPassword:onComplete:success");
promiseWithUser(authResult.getUser(), promise); if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
} }
}) })
.addOnFailureListener(new OnFailureListener() { .addOnFailureListener(new OnFailureListener() {
@ -264,6 +281,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
*/ */
@ReactMethod @ReactMethod
public void signInWithEmailAndPassword(String appName, final String email, final String password, final Promise promise) { 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"); Log.d(TAG, "signInWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -273,7 +299,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override @Override
public void onSuccess(AuthResult authResult) { public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithEmailAndPassword:onComplete:success"); Log.d(TAG, "signInWithEmailAndPassword:onComplete:success");
promiseWithUser(authResult.getUser(), promise); if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
} }
}) })
.addOnFailureListener(new OnFailureListener() { .addOnFailureListener(new OnFailureListener() {
@ -286,14 +316,18 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
/**
* signInWithCustomToken
*
* @param token
* @param promise
*/
@ReactMethod @ReactMethod
public void signInWithCustomToken(String appName, final String token, final Promise promise) { 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"); Log.d(TAG, "signInWithCustomToken");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -303,7 +337,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
@Override @Override
public void onSuccess(AuthResult authResult) { public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithCustomToken:onComplete:success"); Log.d(TAG, "signInWithCustomToken:onComplete:success");
promiseWithUser(authResult.getUser(), promise); if (withData) {
promiseWithAuthResult(authResult, promise);
} else {
promiseWithUser(authResult.getUser(), promise);
}
} }
}) })
.addOnFailureListener(new OnFailureListener() { .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 * .currentUser methods
* ---------------------- */ * ---------------------- */
@ -601,16 +619,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
} }
/**
* signInWithCredential
*
* @param provider
* @param authToken
* @param authSecret
* @param promise
*/
@ReactMethod @ReactMethod
public void signInWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) { 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); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -626,7 +645,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
public void onComplete(@NonNull Task<AuthResult> task) { public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) { if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:onComplete:success"); Log.d(TAG, "signInWithCredential:onComplete:success");
promiseWithUser(task.getResult().getUser(), promise); if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
promiseWithUser(task.getResult().getUser(), promise);
}
} else { } else {
Exception exception = task.getException(); Exception exception = task.getException();
Log.e(TAG, "signInWithCredential:onComplete:failure", exception); Log.e(TAG, "signInWithCredential:onComplete:failure", exception);
@ -954,7 +977,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
/** /**
* link * linkWithCredential
* *
* @param provider * @param provider
* @param authToken * @param authToken
@ -962,7 +985,16 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
* @param promise * @param promise
*/ */
@ReactMethod @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); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -981,7 +1013,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
public void onComplete(@NonNull Task<AuthResult> task) { public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) { if (task.isSuccessful()) {
Log.d(TAG, "link:onComplete:success"); Log.d(TAG, "link:onComplete:success");
promiseWithUser(task.getResult().getUser(), promise); if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
promiseWithUser(task.getResult().getUser(), promise);
}
} else { } else {
Exception exception = task.getException(); Exception exception = task.getException();
Log.e(TAG, "link:onComplete:failure", exception); 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 @ReactMethod
public void unlink(final String appName, final String providerId, final Promise promise) { public void unlink(final String appName, final String providerId, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -1029,16 +1058,8 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
} }
/**
* reauthenticate
*
* @param provider
* @param authToken
* @param authSecret
* @param promise
*/
@ReactMethod @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); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@ -1073,11 +1094,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
/** /**
* Returns an instance of AuthCredential for the specified provider * 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) { private AuthCredential getCredentialForProvider(String provider, String authToken, String authSecret) {
switch (provider) { switch (provider) {
@ -1089,6 +1105,8 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
return TwitterAuthProvider.getCredential(authToken, authSecret); return TwitterAuthProvider.getCredential(authToken, authSecret);
case "github.com": case "github.com":
return GithubAuthProvider.getCredential(authToken); return GithubAuthProvider.getCredential(authToken);
case "oauth":
return OAuthProvider.getCredential(provider, authToken, authSecret);
case "phone": case "phone":
// If the phone number is auto-verified quickly, then the verificationId can be null // 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 // 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 @ReactMethod
public void getToken(String appName, Boolean forceRefresh, final Promise promise) { public void getToken(String appName, Boolean forceRefresh, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@ -1140,11 +1153,6 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
} }
} }
/**
* fetchProvidersForEmail
*
* @param promise
*/
@ReactMethod @ReactMethod
public void fetchProvidersForEmail(String appName, String email, final Promise promise) { public void fetchProvidersForEmail(String appName, String email, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); 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 * 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 * promiseRejectAuthException
* *
@ -1410,6 +1547,14 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
userMap.putArray("providerData", convertProviderData(user.getProviderData(), user)); 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; return userMap;
} }
@ -1451,4 +1596,29 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
eventMap.putMap("state", state); eventMap.putMap("state", state);
Utils.sendEvent(mReactContext, "phone_auth_state_changed", eventMap); 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.DocumentChange;
import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FieldPath;
import com.google.firebase.firestore.FieldValue; import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.FirebaseFirestore; import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.GeoPoint; import com.google.firebase.firestore.GeoPoint;
@ -196,7 +197,7 @@ public class FirestoreSerialize {
} else if (Map.class.isAssignableFrom(value.getClass())) { } else if (Map.class.isAssignableFrom(value.getClass())) {
typeMap.putString("type", "object"); typeMap.putString("type", "object");
typeMap.putMap("value", objectMapToWritable((Map<String, Object>) value)); 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"); typeMap.putString("type", "array");
List<Object> list = (List<Object>) value; List<Object> list = (List<Object>) value;
Object[] array = list.toArray(new Object[list.size()]); Object[] array = list.toArray(new Object[list.size()]);
@ -214,7 +215,6 @@ public class FirestoreSerialize {
typeMap.putString("type", "date"); typeMap.putString("type", "date");
typeMap.putDouble("value", ((Date) value).getTime()); typeMap.putDouble("value", ((Date) value).getTime());
} else { } else {
// TODO: Changed to log an error rather than crash - is this correct?
Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass()); Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass());
typeMap.putString("type", "null"); typeMap.putString("type", "null");
typeMap.putNull("value"); typeMap.putNull("value");
@ -269,6 +269,8 @@ public class FirestoreSerialize {
} else if ("date".equals(type)) { } else if ("date".equals(type)) {
Double time = typeMap.getDouble("value"); Double time = typeMap.getDouble("value");
return new Date(time.longValue()); return new Date(time.longValue());
} else if ("documentid".equals(type)) {
return FieldPath.documentId();
} else if ("fieldvalue".equals(type)) { } else if ("fieldvalue".equals(type)) {
String value = typeMap.getString("value"); String value = typeMap.getString("value");
if ("delete".equals(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.android.gms.tasks.Task;
import com.google.firebase.firestore.DocumentListenOptions; import com.google.firebase.firestore.DocumentListenOptions;
import com.google.firebase.firestore.EventListener; import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FieldPath;
import com.google.firebase.firestore.FirebaseFirestore; import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException; import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.ListenerRegistration; 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.QueryListenOptions;
import com.google.firebase.firestore.QuerySnapshot; import com.google.firebase.firestore.QuerySnapshot;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -128,27 +130,56 @@ public class RNFirebaseFirestoreCollectionReference {
private Query applyFilters(FirebaseFirestore firestore, Query query) { private Query applyFilters(FirebaseFirestore firestore, Query query) {
for (int i = 0; i < filters.size(); i++) { for (int i = 0; i < filters.size(); i++) {
ReadableMap filter = filters.getMap(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"); String operator = filter.getString("operator");
ReadableMap jsValue = filter.getMap("value"); ReadableMap jsValue = filter.getMap("value");
Object value = FirestoreSerialize.parseTypeMap(firestore, jsValue); Object value = FirestoreSerialize.parseTypeMap(firestore, jsValue);
switch (operator) { if (fieldPathType.equals("string")) {
case "EQUAL": String fieldPath = fieldPathMap.getString("string");
query = query.whereEqualTo(fieldPath, value); switch (operator) {
break; case "EQUAL":
case "GREATER_THAN": query = query.whereEqualTo(fieldPath, value);
query = query.whereGreaterThan(fieldPath, value); break;
break; case "GREATER_THAN":
case "GREATER_THAN_OR_EQUAL": query = query.whereGreaterThan(fieldPath, value);
query = query.whereGreaterThanOrEqualTo(fieldPath, value); break;
break; case "GREATER_THAN_OR_EQUAL":
case "LESS_THAN": query = query.whereGreaterThanOrEqualTo(fieldPath, value);
query = query.whereLessThan(fieldPath, value); break;
break; case "LESS_THAN":
case "LESS_THAN_OR_EQUAL": query = query.whereLessThan(fieldPath, value);
query = query.whereLessThanOrEqualTo(fieldPath, value); break;
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; return query;
@ -159,9 +190,17 @@ public class RNFirebaseFirestoreCollectionReference {
for (Object o : ordersList) { for (Object o : ordersList) {
Map<String, Object> order = (Map) o; Map<String, Object> order = (Map) o;
String direction = (String) order.get("direction"); 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; return query;
} }

View File

@ -453,31 +453,15 @@
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"${BUILT_PRODUCTS_DIR}", "${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS", "${SRCROOT}/../../../ios/Pods/**",
"${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",
"${SRCROOT}/../../../ios/Firebase/**", "${SRCROOT}/../../../ios/Firebase/**",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../../react-native/React/**", "$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**", "${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics", "${SRCROOT}/../../../ios/Pods/Headers/Public/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)"; LIBRARY_SEARCH_PATHS = "$(inherited)";
@ -498,31 +482,15 @@
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"${BUILT_PRODUCTS_DIR}", "${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS", "${SRCROOT}/../../../ios/Pods/**",
"${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",
"${SRCROOT}/../../../ios/Firebase/**", "${SRCROOT}/../../../ios/Firebase/**",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../../react-native/React/**", "$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**", "${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics", "${SRCROOT}/../../../ios/Pods/Headers/Public/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)"; LIBRARY_SEARCH_PATHS = "$(inherited)";

View File

@ -3,7 +3,7 @@
#import <Foundation/Foundation.h> #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 AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_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]) { if (![_authStateHandlers valueForKey:firApp.name]) {
FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) { if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self 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 { } 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]) { if (![_idTokenHandlers valueForKey:firApp.name]) {
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) { FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) { if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self 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 { } 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 @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(signInAnonymously: RCT_EXPORT_METHOD(signInAnonymously:(NSString *) appDisplayName
(NSString *) appDisplayName resolver:(RCTPromiseResolveBlock) resolve
resolver: rejecter:(RCTPromiseRejectBlock) reject) {
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
@ -140,7 +137,27 @@ RCT_EXPORT_METHOD(signInAnonymously:
[self promiseWithUser:resolve rejecter:reject user:user]; [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 @param RCTPromiseRejectBlock reject
@return return @return return
*/ */
RCT_EXPORT_METHOD(signInWithEmailAndPassword: RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *) appDisplayName
(NSString *) appDisplayName email:(NSString *) email
email: pass:(NSString *) password
(NSString *) email resolver:(RCTPromiseResolveBlock) resolve
pass: rejecter:(RCTPromiseRejectBlock) reject) {
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
@ -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 createUserWithEmailAndPassword
@ -182,16 +219,11 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return return @return return
*/ */
RCT_EXPORT_METHOD(createUserWithEmailAndPassword: RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *) appDisplayName
(NSString *) appDisplayName email:(NSString *) email
email: pass:(NSString *) password
(NSString *) email resolver:(RCTPromiseResolveBlock) resolve
pass: rejecter:(RCTPromiseRejectBlock) reject) {
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
@ -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 deleteUser
@ -447,18 +505,12 @@ RCT_EXPORT_METHOD(getToken:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(signInWithCredential: RCT_EXPORT_METHOD(signInWithCredential:(NSString *) appDisplayName
(NSString *) appDisplayName provider:(NSString *) provider
provider: token:(NSString *) authToken
(NSString *) provider secret:(NSString *) authSecret
token: resolver:(RCTPromiseResolveBlock) resolve
(NSString *) authToken rejecter:(RCTPromiseRejectBlock) reject) {
secret:
(NSString *) authSecret
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; 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 confirmPasswordReset
@ -610,43 +695,42 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
} }
} }
/** /**
getCurrentUser signInAndRetrieveDataWithCustomToken
@param RCTPromiseResolveBlock resolve @param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(getCurrentUser: RCT_EXPORT_METHOD(signInAndRetrieveDataWithCustomToken:(NSString *) appDisplayName
(NSString *) appDisplayName customToken:(NSString *) customToken
resolver: resolver:(RCTPromiseResolveBlock) resolve
(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; [[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCustomToken:customToken completion:^(FIRAuthDataResult *authResult, NSError *error) {
[self promiseWithUser:resolve rejecter:reject user:user]; if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
} }
/** /**
signInWithCustomToken signInWithCustomToken
@param RCTPromiseResolveBlock resolve @param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(signInWithCustomToken: RCT_EXPORT_METHOD(signInWithCustomToken:(NSString *) appDisplayName
(NSString *) appDisplayName customToken:(NSString *) customToken
customToken: resolver:(RCTPromiseResolveBlock) resolve
(NSString *) customToken rejecter:(RCTPromiseRejectBlock) reject) {
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) { [[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) {
if (error) { if (error) {
[self promiseRejectAuthException:reject error: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 provider
@param NSString authToken @param NSString authToken
@ -746,20 +830,13 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(link: RCT_EXPORT_METHOD(linkWithCredential:(NSString *) appDisplayName
(NSString *) appDisplayName provider:(NSString *) provider
provider: authToken:(NSString *) authToken
(NSString *) provider authSecret:(NSString *) authSecret
authToken: resolver:(RCTPromiseResolveBlock) resolve
(NSString *) authToken rejecter:(RCTPromiseRejectBlock) reject) {
authSecret:
(NSString *) authSecret
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) { 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 unlink
@ -816,7 +931,7 @@ RCT_EXPORT_METHOD(unlink:
} }
/** /**
reauthenticate reauthenticateWithCredential
@param NSString provider @param NSString provider
@param NSString authToken @param NSString authToken
@ -825,7 +940,7 @@ RCT_EXPORT_METHOD(unlink:
@param RCTPromiseRejectBlock reject @param RCTPromiseRejectBlock reject
@return @return
*/ */
RCT_EXPORT_METHOD(reauthenticate: RCT_EXPORT_METHOD(reauthenticateWithCredential:
(NSString *) appDisplayName (NSString *) appDisplayName
provider: provider:
(NSString *) provider (NSString *) provider
@ -914,6 +1029,8 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
credential = [FIRGitHubAuthProvider credentialWithToken:authToken]; credential = [FIRGitHubAuthProvider credentialWithToken:authToken];
} else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) { } else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret]; credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret];
} else if ([provider compare:@"oauth" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIROAuthProvider credentialWithProviderID:@"oauth" IDToken:authToken accessToken:authTokenSecret];
} else { } else {
NSLog(@"Provider not yet handled: %@", provider); NSLog(@"Provider not yet handled: %@", provider);
} }
@ -921,6 +1038,47 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
return credential; 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 // This is here to protect against bugs in the iOS SDK which don't
// correctly refresh the user object when performing certain operations // correctly refresh the user object when performing certain operations
- (void)reloadAndReturnUser:(FIRUser *)user - (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 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; 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 Converts a FIRUser instance into a dictionary to send via RNBridge
@param user FIRUser @param user FIRUser
@return NSDictionary @return NSDictionary
*/ */
- (NSMutableDictionary *)firebaseUserToDict:(FIRUser *)user { - (NSDictionary *)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]; return @{
@"displayName": user.displayName ? user.displayName : [NSNull null],
if ([user valueForKey:@"photoURL"] != nil) { @"email": user.email ? user.email : [NSNull null],
[userDict setValue:[user.photoURL absoluteString] forKey:@"photoURL"]; @"emailVerified": @(user.emailVerified),
} @"isAnonymous": @(user.anonymous),
@"metadata": @{
return userDict; @"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 { - (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSettings {
@ -1188,7 +1400,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
} }
- (NSArray<NSString *> *)supportedEvents { - (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 + (BOOL)requiresMainQueueSetup

View File

@ -93,21 +93,39 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (FIRQuery *)applyFilters:(FIRFirestore *) firestore - (FIRQuery *)applyFilters:(FIRFirestore *) firestore
query:(FIRQuery *) query { query:(FIRQuery *) query {
for (NSDictionary *filter in _filters) { for (NSDictionary *filter in _filters) {
NSString *fieldPath = filter[@"fieldPath"]; NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
NSString *fieldPathType = fieldPathDictionary[@"type"];
NSString *operator = filter[@"operator"]; NSString *operator = filter[@"operator"];
NSDictionary *jsValue = filter[@"value"]; NSDictionary *jsValue = filter[@"value"];
id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue]; id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue];
if ([operator isEqualToString:@"EQUAL"]) { if ([fieldPathType isEqualToString:@"string"]) {
query = [query queryWhereField:fieldPath isEqualTo:value]; NSString *fieldPath = fieldPathDictionary[@"string"];
} else if ([operator isEqualToString:@"GREATER_THAN"]) { if ([operator isEqualToString:@"EQUAL"]) {
query = [query queryWhereField:fieldPath isGreaterThan:value]; query = [query queryWhereField:fieldPath isEqualTo:value];
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) { } else if ([operator isEqualToString:@"GREATER_THAN"]) {
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value]; query = [query queryWhereField:fieldPath isGreaterThan:value];
} else if ([operator isEqualToString:@"LESS_THAN"]) { } else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isLessThan:value]; query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) { } else if ([operator isEqualToString:@"LESS_THAN"]) {
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value]; 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; return query;
@ -116,9 +134,17 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (FIRQuery *)applyOrders:(FIRQuery *) query { - (FIRQuery *)applyOrders:(FIRQuery *) query {
for (NSDictionary *order in _orders) { for (NSDictionary *order in _orders) {
NSString *direction = order[@"direction"]; 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; return query;
} }

View File

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

View File

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

View File

@ -45,6 +45,12 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
content.userInfo = details; content.userInfo = details;
content.badge = [RCTConvert NSNumber:details[@"badge"]]; 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"]]; NSDate *fireDate = [RCTConvert NSDate:details[@"fire_date"]];
if(fireDate == nil){ if(fireDate == nil){

View File

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

View File

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

693
lib/index.d.ts vendored
View File

@ -5,80 +5,77 @@
declare module "react-native-firebase" { declare module "react-native-firebase" {
type AuthProvider = { /** 3rd party provider Credentials */
PROVIDER_ID: string, type AuthCredential = {
credential: (token: string, secret?: string) => object, providerId: string,
}; token: string,
secret: string
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;
} }
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 { interface RnError extends Error {
code?: string; code?: string;
} }
@ -476,6 +473,15 @@ declare module "react-native-firebase" {
update(values: Object, onComplete?: (a: RnError | null) => any): Promise<any>; 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; [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 { interface User {
/** /**
* The user's display name (if available). * The user's display name (if available).
@ -542,6 +579,10 @@ declare module "react-native-firebase" {
* *
*/ */
isAnonymous: boolean isAnonymous: boolean
metadata: UserMetadata
phoneNumber: string | null
/** /**
* - The URL of the user's profile picture (if available). * - 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. * - Additional provider-specific information about the user.
*/ */
providerData: any | null providerData: Array<UserInfo>
/** /**
* - The authentication provider ID for the current user. * - The authentication provider ID for the current user.
* For example, 'facebook.com', or 'google.com'. * For example, 'facebook.com', or 'google.com'.
*/ */
providerId: string | null providerId: string
/** /**
* - The user's unique ID. * - The user's unique ID.
*/ */
@ -567,18 +608,26 @@ declare module "react-native-firebase" {
/** /**
* Returns the users authentication token. * Returns the users authentication token.
*
* @param forceRefresh: boolean - default to false
*/ */
getToken(): Promise<string> getIdToken(forceRefresh?: boolean): Promise<string>
/** getToken(forceRefresh?: boolean): Promise<string>
* Reauthenticate the current user with credentials:
*/ linkAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential>
reauthenticate(credential: Credential): Promise<void>
/** /**
* Link the user with a 3rd party credential provider. * 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. * Refreshes the current user.
@ -589,7 +638,11 @@ declare module "react-native-firebase" {
* Sends a verification email to a user. * Sends a verification email to a user.
* This will Promise reject is the user is anonymous. * 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. * Updates the user's email address.
@ -609,57 +662,114 @@ declare module "react-native-firebase" {
* Updates a user's profile data. * Updates a user's profile data.
* Profile data should be an object of fields to update: * Profile data should be an object of fields to update:
*/ */
updateProfile(profile: Object): Promise<void> updateProfile(updates: UpdateProfile): Promise<void>
} }
/** 3rd party provider Credentials */ type ActionCodeSettings = {
interface Credential { android: {
provider: string, installApp?: boolean,
token: string, minimumVersion?: string,
secret: string packageName: string,
},
handleCodeInApp?: boolean,
iOS: {
bundleId?: string,
},
url: string,
} }
interface ActionCodeInfo { interface ActionCodeInfo {
email: string, data: {
error: string, email?: string,
fromEmail: string, fromEmail?: string
verifyEmail: string, },
recoverEmail: string, operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL'
passwordReset: string }
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 { namespace auth {
type AuthResult = {
authenticated: boolean,
user: object | null
} | null;
type AuthProvider = {
PROVIDER_ID: string,
credential: (token: string, secret?: string) => AuthCredential,
};
interface Auth { interface Auth {
readonly app: App;
/** /**
* Returns the current Firebase authentication state. * 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. * Returns the currently signed-in user (or null). See the User class documentation for further usage.
*/ */
currentUser: User | null 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). * Listen for changes in the users auth state (logging in and out).
* This method returns a unsubscribe function to stop listening to events. * 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. * 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, onAuthStateChanged(listener: Function): () => void;
completed?: () => any): () => any;
/** /**
* We can create a user by calling the createUserWithEmailAndPassword() function. * Listen for changes in id token.
* The method accepts two parameters, an email and a password. * 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. * Listen for changes in the user.
* It accepts two parameters, the user's email and password: * 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. * Sign an anonymous user.
@ -667,11 +777,23 @@ declare module "react-native-firebase" {
*/ */
signInAnonymously(): Promise<User> signInAnonymously(): Promise<User>
createUserAndRetrieveDataWithEmailAndPassword(email: string, password: string): Promise<UserCredential>
/** /**
* Sign in the user with a 3rd party credential provider. * We can create a user by calling the createUserWithEmailAndPassword() function.
* credential requires the following properties: * 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. * Sign a user in with a self-signed JWT token.
@ -681,22 +803,42 @@ declare module "react-native-firebase" {
*/ */
signInWithCustomToken(token: string): Promise<User> 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. * Sends a password reset email to the given email address.
* Unlike the web SDK, * Unlike the web SDK,
* the email will contain a password reset link rather than a code. * 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. * 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. * 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. * 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> checkActionCode(code: string): Promise<ActionCodeInfo>
/** /**
* Completes the password reset process, * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
* given a confirmation code and new password.
*/ */
signOut(): Promise<void> fetchProvidersForEmail(email: string): Promise<Array<string>>
verifyPasswordResetCode(code: string): Promise<string>
[key: string]: any; [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 { namespace messaging {
@ -895,5 +1054,315 @@ declare module "react-native-firebase" {
setUserIdentifier(userId: string): void; 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, ...ViewPropTypes,
size: PropTypes.string.isRequired, size: PropTypes.string.isRequired,
unitId: PropTypes.string.isRequired, unitId: PropTypes.string.isRequired,
/* eslint-disable react/forbid-prop-types */
request: PropTypes.object, request: PropTypes.object,
video: 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; adMobPropTypes[eventType] = PropTypes.func;
}); });
Object.keys(NativeExpressEventTypes).forEach((eventType) => { Object.keys(NativeExpressEventTypes).forEach(eventType => {
adMobPropTypes[eventType] = PropTypes.func; 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 { export default class AdRequest {
constructor() { constructor() {
this._props = { this._props = {
keywords: [], keywords: [],
@ -12,7 +11,7 @@ export default class AdRequest {
} }
addTestDevice(deviceId?: string) { addTestDevice(deviceId?: string) {
this._props.testDevices.push(deviceId ? deviceId : 'DEVICE_ID_EMULATOR'); this._props.testDevices.push(deviceId || 'DEVICE_ID_EMULATOR');
return this; return this;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +1,29 @@
import { NativeModules } from 'react-native';
import { statics } from './'; import { statics } from './';
import AdRequest from './AdRequest'; import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events'; import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import { nativeToJSError } from '../../utils'; import { nativeToJSError } from '../../utils';
import type AdMob from './';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
let subscriptions = []; let subscriptions = [];
export default class RewardedVideo { 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++) { for (let i = 0, len = subscriptions.length; i < len; i++) {
subscriptions[i].remove(); subscriptions[i].remove();
} }
subscriptions = []; subscriptions = [];
this.admob = admob; this._admob = admob;
this.adUnit = adUnit; this.adUnit = adUnit;
this.loaded = false; this.loaded = false;
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`); 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 * @param event
* @private * @private
*/ */
_onRewardedVideoEvent = (event) => { _onRewardedVideoEvent = event => {
const eventType = `rewarded_video:${this.adUnit}:${event.type}`; const eventType = `rewarded_video:${this.adUnit}:${event.type}`;
let emitData = Object.assign({}, event); let emitData = Object.assign({}, event);
@ -60,7 +63,10 @@ export default class RewardedVideo {
adRequest = new AdRequest().addTestDevice().build(); 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() { show() {
if (this.loaded) { if (this.loaded) {
FirebaseAdMob.rewardedVideoShowAd(this.adUnit); getNativeModule(this._admob).rewardedVideoShowAd(this.adUnit);
} }
} }
@ -94,11 +100,18 @@ export default class RewardedVideo {
}; };
if (!types[eventType]) { 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; 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); subscriptions.push(sub);
return sub; return sub;
} }

View File

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

View File

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

View File

@ -32,6 +32,7 @@ export default class Analytics extends ModuleBase {
constructor(app: App) { constructor(app: App) {
super(app, { super(app, {
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
} }
@ -45,16 +46,21 @@ export default class Analytics extends ModuleBase {
logEvent(name: string, params: Object = {}): void { logEvent(name: string, params: Object = {}): void {
// check name is not a reserved event name // check name is not a reserved event name
if (ReservedEventNames.includes(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 // name format validation
if (!AlphaNumericUnderscore.test(name)) { 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 // 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 // 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 // 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 * @param object
*/ */
setUserProperties(object: Object): void { setUserProperties(object: Object): void {
for (const property of Object.keys(object)) { Object.keys(object).forEach(property => {
getNativeModule(this).setUserProperty(property, object[property]); getNativeModule(this).setUserProperty(property, object[property]);
} });
} }
} }

View File

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

View File

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

View File

@ -6,19 +6,13 @@ import INTERNALS from '../../utils/internals';
import { getNativeModule } from '../../utils/native'; import { getNativeModule } from '../../utils/native';
import type Auth from './'; import type Auth from './';
import type { ActionCodeSettings, AuthCredential } from '../../types'; import type {
ActionCodeSettings,
type NativeUser = { AuthCredential,
displayName?: string, NativeUser,
email?: string, UserCredential,
emailVerified?: boolean, UserMetadata,
isAnonymous?: boolean, } from './types';
phoneNumber?: string,
photoURL?: string,
providerData: UserInfo[],
providerId: string,
uid: string,
}
type UserInfo = { type UserInfo = {
displayName?: string, displayName?: string,
@ -27,7 +21,12 @@ type UserInfo = {
photoURL?: string, photoURL?: string,
providerId: string, providerId: string,
uid: string, uid: string,
} };
type UpdateProfile = {
displayName?: string,
photoURL?: string,
};
export default class User { export default class User {
_auth: Auth; _auth: Auth;
@ -63,6 +62,10 @@ export default class User {
return this._user.isAnonymous || false; return this._user.isAnonymous || false;
} }
get metadata(): UserMetadata {
return this._user.metadata;
}
get phoneNumber(): ?string { get phoneNumber(): ?string {
return this._user.phoneNumber || null; return this._user.phoneNumber || null;
} }
@ -92,8 +95,11 @@ export default class User {
* @return {Promise} * @return {Promise}
*/ */
delete(): Promise<void> { delete(): Promise<void> {
return this._auth return getNativeModule(this._auth)
._interceptUndefinedUserValue(getNativeModule(this._auth).delete()); .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 * @param credential
*/ */
linkWithCredential(credential: AuthCredential): Promise<User> { linkWithCredential(credential: AuthCredential): Promise<User> {
return this._auth console.warn(
._interceptUserValue(getNativeModule(this._auth).link(credential.providerId, credential.token, credential.secret)); '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 * @return {Promise} A promise resolved upon completion
*/ */
reauthenticateWithCredential(credential: AuthCredential): Promise<void> { reauthenticateWithCredential(credential: AuthCredential): Promise<void> {
return this._auth console.warn(
._interceptUndefinedUserValue(getNativeModule(this._auth).reauthenticate(credential.providerId, credential.token, credential.secret)); '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} * @return {Promise}
*/ */
reload(): Promise<void> { reload(): Promise<void> {
return this._auth return getNativeModule(this._auth)
._interceptUndefinedUserValue(getNativeModule(this._auth).reload()); .reload()
.then(user => {
this._auth._setUser(user);
});
} }
/** /**
* Send verification email to current user. * Send verification email to current user.
*/ */
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void> { sendEmailVerification(
return this._auth actionCodeSettings?: ActionCodeSettings
._interceptUndefinedUserValue(getNativeModule(this._auth).sendEmailVerification(actionCodeSettings)); ): Promise<void> {
return getNativeModule(this._auth)
.sendEmailVerification(actionCodeSettings)
.then(user => {
this._auth._setUser(user);
});
} }
toJSON(): Object { toJSON(): Object {
@ -149,7 +225,9 @@ export default class User {
* @return {Promise.<TResult>|*} * @return {Promise.<TResult>|*}
*/ */
unlink(providerId: string): Promise<User> { 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 * @return {Promise} A promise resolved upon completion
*/ */
updateEmail(email: string): Promise<void> { updateEmail(email: string): Promise<void> {
return this._auth return getNativeModule(this._auth)
._interceptUndefinedUserValue(getNativeModule(this._auth).updateEmail(email)); .updateEmail(email)
.then(user => {
this._auth._setUser(user);
});
} }
/** /**
@ -169,8 +250,11 @@ export default class User {
* @return {Promise} * @return {Promise}
*/ */
updatePassword(password: string): Promise<void> { updatePassword(password: string): Promise<void> {
return this._auth return getNativeModule(this._auth)
._interceptUndefinedUserValue(getNativeModule(this._auth).updatePassword(password)); .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) * @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} * @return {Promise}
*/ */
updateProfile(updates: Object = {}): Promise<void> { updateProfile(updates: UpdateProfile = {}): Promise<void> {
return this._auth return getNativeModule(this._auth)
._interceptUndefinedUserValue(getNativeModule(this._auth).updateProfile(updates)); .updateProfile(updates)
} .then(user => {
this._auth._setUser(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);
} }
/** /**
* KNOWN UNSUPPORTED METHODS * KNOWN UNSUPPORTED METHODS
*/ */
linkAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkAndRetrieveDataWithCredential'));
}
linkWithPhoneNumber() { linkWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPhoneNumber')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithPhoneNumber'
)
);
} }
linkWithPopup() { linkWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup')
);
} }
linkWithRedirect() { linkWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithRedirect')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithRedirect'
)
);
} }
reauthenticateWithPhoneNumber() { reauthenticateWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPhoneNumber')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPhoneNumber'
)
);
} }
reauthenticateWithPopup() { reauthenticateWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPopup')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPopup'
)
);
} }
reauthenticateWithRedirect() { reauthenticateWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithRedirect'
)
);
} }
updatePhoneNumber() { 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 { 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 PhoneAuthProvider from './providers/PhoneAuthProvider';
import GoogleAuthProvider from './providers/GoogleAuthProvider'; import GoogleAuthProvider from './providers/GoogleAuthProvider';
import GithubAuthProvider from './providers/GithubAuthProvider'; import GithubAuthProvider from './providers/GithubAuthProvider';
import OAuthProvider from './providers/OAuthProvider';
import TwitterAuthProvider from './providers/TwitterAuthProvider'; import TwitterAuthProvider from './providers/TwitterAuthProvider';
import FacebookAuthProvider from './providers/FacebookAuthProvider'; import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener'; 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'; import type App from '../core/firebase-app';
type AuthResult = { type AuthState = {
authenticated: boolean, user?: NativeUser,
user: Object|null };
} | null;
type ActionCodeInfo = {
data: {
email?: string,
fromEmail?: string,
},
operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL',
};
const NATIVE_EVENTS = [ const NATIVE_EVENTS = [
'auth_state_changed', 'auth_state_changed',
'auth_id_token_changed',
'phone_auth_state_changed', 'phone_auth_state_changed',
]; ];
@ -37,99 +52,79 @@ export const MODULE_NAME = 'RNFirebaseAuth';
export const NAMESPACE = 'auth'; export const NAMESPACE = 'auth';
export default class Auth extends ModuleBase { export default class Auth extends ModuleBase {
_authResult: AuthResult | null; _authResult: boolean;
_languageCode: string;
_user: User | null; _user: User | null;
constructor(app: App) { constructor(app: App) {
super(app, { super(app, {
events: NATIVE_EVENTS, events: NATIVE_EVENTS,
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
this._user = null; this._user = null;
this._authResult = null; this._authResult = false;
this._languageCode =
getNativeModule(this).APP_LANGUAGE[app._name] ||
getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onAuthStateChanged // public event name: onAuthStateChanged
getAppEventName(this, 'auth_state_changed'), getAppEventName(this, 'auth_state_changed'),
this._onInternalAuthStateChanged.bind(this), (state: AuthState) => {
this._setUser(state.user);
SharedEventEmitter.emit(
getAppEventName(this, 'onAuthStateChanged'),
this._user
);
}
); );
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public events based on event.type // public events based on event.type
getAppEventName(this, 'phone_auth_state_changed'), 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( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onIdTokenChanged // public event name: onIdTokenChanged
getAppEventName(this, 'auth_id_token_changed'), 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).addAuthStateListener();
getNativeModule(this).addIdTokenListener(); getNativeModule(this).addIdTokenListener();
} }
/** _setUser(user: ?NativeUser): ?User {
* Route a phone state change event to the correct listeners this._authResult = true;
* @param event this._user = user ? new User(this, user) : null;
* @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;
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user); SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
return this._user;
} }
/** _setUserCredential(userCredential: NativeUserCredential): UserCredential {
* Internal auth changed listener const user = new User(this, userCredential.user);
* @param auth this._authResult = true;
* @private this._user = user;
*/ SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
_onInternalAuthStateChanged(auth: AuthResult) { return {
this._setAuthState(auth); additionalUserInfo: userCredential.additionalUserInfo,
SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user); 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(() => {});
} }
/* /*
@ -142,18 +137,19 @@ export default class Auth extends ModuleBase {
*/ */
onAuthStateChanged(listener: Function) { onAuthStateChanged(listener: Function) {
getLogger(this).info('Creating onAuthStateChanged listener'); 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); if (this._authResult) listener(this._user || null);
return this._offAuthStateChanged.bind(this, listener);
}
/** return () => {
* Remove auth change listener getLogger(this).info('Removing onAuthStateChanged listener');
* @param listener SharedEventEmitter.removeListener(
*/ getAppEventName(this, 'onAuthStateChanged'),
_offAuthStateChanged(listener: Function) { listener
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) { onIdTokenChanged(listener: Function) {
getLogger(this).info('Creating onIdTokenChanged listener'); 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); if (this._authResult) listener(this._user || null);
return this._offIdTokenChanged.bind(this, listener);
}
/** return () => {
* Remove id token change listener getLogger(this).info('Removing onIdTokenChanged listener');
* @param listener SharedEventEmitter.removeListener(
*/ getAppEventName(this, 'onIdTokenChanged'),
_offIdTokenChanged(listener: Function) { listener
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) { onUserChanged(listener: Function) {
getLogger(this).info('Creating onUserChanged listener'); 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); if (this._authResult) listener(this._user || null);
return this._offUserChanged.bind(this, listener);
}
/** return () => {
* Remove user change listener getLogger(this).info('Removing onUserChanged listener');
* @param listener SharedEventEmitter.removeListener(
*/ getAppEventName(this, 'onUserChanged'),
_offUserChanged(listener: Function) { listener
getLogger(this).info('Removing onUserChanged listener'); );
SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener); };
} }
/** /**
@ -201,15 +199,54 @@ export default class Auth extends ModuleBase {
* @return {Promise} * @return {Promise}
*/ */
signOut(): Promise<void> { 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 * Sign a user in anonymously
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInAnonymously(): Promise<User> { signInAnonymouslyAndRetrieveData(): Promise<UserCredential> {
return this._interceptUserValue(getNativeModule(this).signInAnonymously()); 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 * @param {string} password The user's password
* @return {Promise} A promise indicating the completion * @return {Promise} A promise indicating the completion
*/ */
createUserWithEmailAndPassword(email: string, password: string): Promise<User> { createUserAndRetrieveDataWithEmailAndPassword(
return this._interceptUserValue(getNativeModule(this).createUserWithEmailAndPassword(email, password)); 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 * @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion * @return {Promise} A promise that is resolved upon completion
*/ */
signInWithEmailAndPassword(email: string, password: string): Promise<User> { signInAndRetrieveDataWithEmailAndPassword(
return this._interceptUserValue(getNativeModule(this).signInWithEmailAndPassword(email, password)); 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. * @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInWithCustomToken(customToken: string): Promise<User> { signInAndRetrieveDataWithCustomToken(
return this._interceptUserValue(getNativeModule(this).signInWithCustomToken(customToken)); 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 * Sign the user in with a third-party authentication provider
* @return {Promise} A promise resolved upon completion * @return {Promise} A promise resolved upon completion
*/ */
signInWithCredential(credential: AuthCredential): Promise<User> { signInAndRetrieveDataWithCredential(
return this._interceptUserValue( credential: AuthCredential
getNativeModule(this).signInWithCredential( ): Promise<UserCredential> {
credential.providerId, credential.token, credential.secret, 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> { signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return getNativeModule(this).signInWithPhoneNumber(phoneNumber).then((result) => { return getNativeModule(this)
return new ConfirmationResult(this, result.verificationId); .signInWithPhoneNumber(phoneNumber)
}); .then(result => new ConfirmationResult(this, result.verificationId));
} }
/** /**
@ -272,7 +376,10 @@ export default class Auth extends ModuleBase {
* @param autoVerifyTimeout Android Only * @param autoVerifyTimeout Android Only
* @returns {PhoneAuthListener} * @returns {PhoneAuthListener}
*/ */
verifyPhoneNumber(phoneNumber: string, autoVerifyTimeout?: number): PhoneAuthListener { verifyPhoneNumber(
phoneNumber: string,
autoVerifyTimeout?: number
): PhoneAuthListener {
return new PhoneAuthListener(this, phoneNumber, autoVerifyTimeout); return new PhoneAuthListener(this, phoneNumber, autoVerifyTimeout);
} }
@ -280,8 +387,14 @@ export default class Auth extends ModuleBase {
* Send reset password instructions via email * Send reset password instructions via email
* @param {string} email The email to send password reset instructions * @param {string} email The email to send password reset instructions
*/ */
sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void> { sendPasswordResetEmail(
return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings); email: string,
actionCodeSettings?: ActionCodeSettings
): Promise<void> {
return getNativeModule(this).sendPasswordResetEmail(
email,
actionCodeSettings
);
} }
/** /**
@ -314,26 +427,32 @@ export default class Auth extends ModuleBase {
* @param code * @param code
* @return {Promise.<any>|Promise<ActionCodeInfo>} * @return {Promise.<any>|Promise<ActionCodeInfo>}
*/ */
checkActionCode(code: string): Promise<void> { checkActionCode(code: string): Promise<ActionCodeInfo> {
return getNativeModule(this).checkActionCode(code); 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). * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
* @return {Promise} * @return {Promise}
*/ */
fetchProvidersForEmail(email: string): Promise<Array<String>> { fetchProvidersForEmail(email: string): Promise<string[]> {
return getNativeModule(this).fetchProvidersForEmail(email); 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 * Get the currently signed in user
* @return {Promise} * @return {Promise}
@ -342,28 +461,58 @@ export default class Auth extends ModuleBase {
return this._user; return this._user;
} }
get languageCode(): string {
return this._languageCode;
}
/** /**
* KNOWN UNSUPPORTED METHODS * KNOWN UNSUPPORTED METHODS
*/ */
getRedirectResult() { getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'getRedirectResult'
)
);
} }
setPersistence() { setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence')); throw new Error(
} INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
signInAndRetrieveDataWithCredential() { 'setPersistence'
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInAndRetrieveDataWithCredential')); )
);
} }
signInWithPopup() { signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'signInWithPopup'
)
);
} }
signInWithRedirect() { signInWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithRedirect')); throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'signInWithRedirect'
)
);
}
// 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, GithubAuthProvider,
TwitterAuthProvider, TwitterAuthProvider,
FacebookAuthProvider, FacebookAuthProvider,
OAuthProvider,
PhoneAuthState: { PhoneAuthState: {
CODE_SENT: 'sent', CODE_SENT: 'sent',
AUTO_VERIFY_TIMEOUT: 'timeout', AUTO_VERIFY_TIMEOUT: 'timeout',

View File

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

View File

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

View File

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

View File

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

View File

@ -2,13 +2,15 @@
* @flow * @flow
* TwitterAuthProvider representation wrapper * TwitterAuthProvider representation wrapper
*/ */
import type { AuthCredential } from '../../../types'; import type { AuthCredential } from '../types';
const providerId = 'twitter.com'; const providerId = 'twitter.com';
export default class TwitterAuthProvider { export default class TwitterAuthProvider {
constructor() { 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 { 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, numberValue?: number,
dataValue?: Object, dataValue?: Object,
boolValue?: boolean, boolValue?: boolean,
source: 'remoteConfigSourceRemote' | 'remoteConfigSourceDefault' | ' remoteConfigSourceStatic', source:
} | 'remoteConfigSourceRemote'
| 'remoteConfigSourceDefault'
| ' remoteConfigSourceStatic',
};
export const MODULE_NAME = 'RNFirebaseRemoteConfig'; export const MODULE_NAME = 'RNFirebaseRemoteConfig';
export const NAMESPACE = 'config'; export const NAMESPACE = 'config';
@ -28,6 +31,7 @@ export default class RemoteConfig extends ModuleBase {
constructor(app: App) { constructor(app: App) {
super(app, { super(app, {
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
this._developerModeEnabled = false; this._developerModeEnabled = false;
@ -43,9 +47,26 @@ export default class RemoteConfig extends ModuleBase {
return { return {
source: nativeValue.source, source: nativeValue.source,
val() { val() {
if (nativeValue.boolValue !== null && (nativeValue.stringValue === 'true' || nativeValue.stringValue === 'false' || nativeValue.stringValue === null)) return nativeValue.boolValue; if (
if (nativeValue.numberValue !== null && nativeValue.numberValue !== undefined && (nativeValue.stringValue == null || nativeValue.stringValue === '' || nativeValue.numberValue.toString() === nativeValue.stringValue)) return nativeValue.numberValue; nativeValue.boolValue !== null &&
if (nativeValue.dataValue !== nativeValue.stringValue && (nativeValue.stringValue == null || nativeValue.stringValue === '')) return nativeValue.dataValue; (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; return nativeValue.stringValue;
}, },
}; };
@ -69,7 +90,9 @@ export default class RemoteConfig extends ModuleBase {
*/ */
fetch(expiration?: number) { fetch(expiration?: number) {
if (expiration !== undefined) { 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); return getNativeModule(this).fetchWithExpirationDuration(expiration);
} }
getLogger(this).debug('Fetching remote config data'); getLogger(this).debug('Fetching remote config data');
@ -123,7 +146,7 @@ export default class RemoteConfig extends ModuleBase {
getValues(keys: Array<String>) { getValues(keys: Array<String>) {
return getNativeModule(this) return getNativeModule(this)
.getValues(keys || []) .getValues(keys || [])
.then((nativeValues) => { .then(nativeValues => {
const values: { [String]: Object } = {}; const values: { [String]: Object } = {};
for (let i = 0, len = keys.length; i < len; i++) { for (let i = 0, len = keys.length; i < len; i++) {
values[keys[i]] = this._nativeValueToJS(nativeValues[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 Analytics, { NAMESPACE as AnalyticsNamespace } from '../analytics';
import Config, { NAMESPACE as ConfigNamespace } from '../config'; import Config, { NAMESPACE as ConfigNamespace } from '../config';
import Crash, { NAMESPACE as CrashNamespace } from '../crash'; 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 Database, { NAMESPACE as DatabaseNamespace } from '../database';
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore'; import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
import Links, { NAMESPACE as LinksNamespace } from '../links'; 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 Storage, { NAMESPACE as StorageNamespace } from '../storage';
import Utils, { NAMESPACE as UtilsNamespace } from '../utils'; import Utils, { NAMESPACE as UtilsNamespace } from '../utils';
import type { import type { FirebaseOptions } from '../../types';
FirebaseOptions,
} from '../../types';
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
export default class App { export default class App {
_extendedProps: { [string] : boolean }; _extendedProps: { [string]: boolean };
_initialized: boolean = false; _initialized: boolean = false;
_name: string; _name: string;
_nativeInitialized: boolean = false; _nativeInitialized: boolean = false;
@ -51,7 +50,11 @@ export default class App {
storage: () => Storage; storage: () => Storage;
utils: () => Utils; utils: () => Utils;
constructor(name: string, options: FirebaseOptions, fromNative: boolean = false) { constructor(
name: string,
options: FirebaseOptions,
fromNative: boolean = false
) {
this._name = name; this._name = name;
this._options = Object.assign({}, options); this._options = Object.assign({}, options);
@ -59,10 +62,14 @@ export default class App {
this._initialized = true; this._initialized = true;
this._nativeInitialized = true; this._nativeInitialized = true;
} else if (options.databaseURL && options.apiKey) { } else if (options.databaseURL && options.apiKey) {
FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => { FirebaseCoreModule.initializeApp(
this._initialized = true; this._name,
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result }); this._options,
}); (error, result) => {
this._initialized = true;
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
}
);
} }
// modules // modules
@ -109,7 +116,10 @@ export default class App {
* @param props * @param props
*/ */
extendApp(props: Object) { 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); const keys = Object.keys(props);
for (let i = 0, len = keys.length; i < len; i++) { for (let i = 0, len = keys.length; i < len; i++) {
@ -130,7 +140,9 @@ export default class App {
* @return {Promise} * @return {Promise}
*/ */
delete() { 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 // 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) { // if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
// return Promise.reject( // return Promise.reject(
@ -141,7 +153,6 @@ export default class App {
// return FirebaseCoreModule.deleteApp(this._name); // return FirebaseCoreModule.deleteApp(this._name);
} }
/** /**
* *
* @return {*} * @return {*}

View File

@ -7,21 +7,58 @@ import { NativeModules } from 'react-native';
import APPS from '../../utils/apps'; import APPS from '../../utils/apps';
import INTERNALS from '../../utils/internals'; import INTERNALS from '../../utils/internals';
import App from './firebase-app'; import App from './firebase-app';
import VERSION from '../../version';
// module imports // 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 AuthStatics, MODULE_NAME as AuthModuleName } from '../auth';
import { statics as AnalyticsStatics, MODULE_NAME as AnalyticsModuleName } from '../analytics'; import {
import { statics as ConfigStatics, MODULE_NAME as ConfigModuleName } from '../config'; statics as AnalyticsStatics,
import { statics as CrashStatics, MODULE_NAME as CrashModuleName } from '../crash'; MODULE_NAME as AnalyticsModuleName,
import { statics as CrashlyticsStatics, MODULE_NAME as CrashlyticsModuleName } from '../fabric/crashlytics'; } from '../analytics';
import { statics as DatabaseStatics, MODULE_NAME as DatabaseModuleName } from '../database'; import {
import { statics as FirestoreStatics, MODULE_NAME as FirestoreModuleName } from '../firestore'; statics as ConfigStatics,
import { statics as LinksStatics, MODULE_NAME as LinksModuleName } from '../links'; MODULE_NAME as ConfigModuleName,
import { statics as MessagingStatics, MODULE_NAME as MessagingModuleName } from '../messaging'; } from '../config';
import { statics as PerformanceStatics, MODULE_NAME as PerfModuleName } from '../perf'; import {
import { statics as StorageStatics, MODULE_NAME as StorageModuleName } from '../storage'; statics as CrashStatics,
import { statics as UtilsStatics, MODULE_NAME as UtilsModuleName } from '../utils'; 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 { import type {
AdMobModule, AdMobModule,
@ -59,25 +96,57 @@ class Firebase {
constructor() { constructor() {
if (!FirebaseCoreModule) { if (!FirebaseCoreModule) {
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE)); throw new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE);
} }
APPS.initializeNativeApps(); APPS.initializeNativeApps();
// modules // modules
this.admob = APPS.moduleAndStatics('admob', AdMobStatics, AdmobModuleName); 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.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.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
this.database = APPS.moduleAndStatics('database', DatabaseStatics, DatabaseModuleName); this.database = APPS.moduleAndStatics(
'database',
DatabaseStatics,
DatabaseModuleName
);
this.fabric = { 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.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
this.messaging = APPS.moduleAndStatics('messaging', MessagingStatics, MessagingModuleName); this.messaging = APPS.moduleAndStatics(
this.perf = APPS.moduleAndStatics('perf', PerformanceStatics, PerfModuleName); 'messaging',
this.storage = APPS.moduleAndStatics('storage', StorageStatics, StorageModuleName); MessagingStatics,
MessagingModuleName
);
this.perf = APPS.moduleAndStatics(
'perf',
PerformanceStatics,
PerfModuleName
);
this.storage = APPS.moduleAndStatics(
'storage',
StorageStatics,
StorageModuleName
);
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName); this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName);
} }
@ -112,6 +181,14 @@ class Firebase {
get apps(): Array<App> { get apps(): Array<App> {
return APPS.apps(); return APPS.apps();
} }
/**
* The current SDK version.
* @return {string}
*/
get SDK_VERSION(): string {
return VERSION;
}
} }
export default new Firebase(); export default new Firebase();

View File

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

View File

@ -7,7 +7,6 @@ import { getNativeModule } from '../../utils/native';
import type Database from './'; import type Database from './';
import type Reference from './reference'; import type Reference from './reference';
/** /**
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect * @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect
* @class Disconnect * @class Disconnect
@ -33,7 +32,10 @@ export default class Disconnect {
* @returns {*} * @returns {*}
*/ */
set(value: string | Object): Promise<void> { 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 {*} * @returns {*}
*/ */
update(values: Object): Promise<void> { 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, { super(app, {
events: NATIVE_EVENTS, events: NATIVE_EVENTS,
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
this._transactionHandler = new TransactionHandler(this); this._transactionHandler = new TransactionHandler(this);
@ -46,7 +47,7 @@ export default class Database extends ModuleBase {
setTimeout(() => { setTimeout(() => {
this._serverTimeOffset = 0; this._serverTimeOffset = 0;
this._offsetRef = this.ref('.info/serverTimeOffset'); this._offsetRef = this.ref('.info/serverTimeOffset');
this._offsetRef.on('value', (snapshot) => { this._offsetRef.on('value', snapshot => {
this._serverTimeOffset = snapshot.val() || this._serverTimeOffset; this._serverTimeOffset = snapshot.val() || this._serverTimeOffset;
}); });
}, 1); }, 1);
@ -85,9 +86,13 @@ export default class Database extends ModuleBase {
} }
export const statics = { export const statics = {
ServerValue: NativeModules.RNFirebaseDatabase ? { ServerValue: NativeModules.RNFirebaseDatabase
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' }, ? {
} : {}, TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || {
'.sv': 'timestamp',
},
}
: {},
enableLogging(enabled: boolean) { enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) { if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled); NativeModules[MODULE_NAME].enableLogging(enabled);

View File

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

View File

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

View File

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

View File

@ -14,9 +14,7 @@ let transactionId = 0;
* @returns {number} * @returns {number}
* @private * @private
*/ */
const generateTransactionId = (): number => { const generateTransactionId = (): number => transactionId++;
return transactionId++;
};
/** /**
* @class TransactionHandler * @class TransactionHandler
@ -24,7 +22,7 @@ const generateTransactionId = (): number => {
export default class TransactionHandler { export default class TransactionHandler {
_database: Database; _database: Database;
_transactionListener: Function; _transactionListener: Function;
_transactions: { [number]: Object } _transactions: { [number]: Object };
constructor(database: Database) { constructor(database: Database) {
this._transactions = {}; this._transactions = {};
@ -32,7 +30,7 @@ export default class TransactionHandler {
this._transactionListener = SharedEventEmitter.addListener( this._transactionListener = SharedEventEmitter.addListener(
getAppEventName(this._database, 'database_transaction_event'), 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 onComplete
* @param applyLocally * @param applyLocally
*/ */
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) { add(
reference: Object,
transactionUpdater: Function,
onComplete?: Function,
applyLocally?: boolean = false
) {
const id = generateTransactionId(); const id = generateTransactionId();
this._transactions[id] = { this._transactions[id] = {
@ -56,7 +59,11 @@ export default class TransactionHandler {
started: true, 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': case 'complete':
return this._handleComplete(event); return this._handleComplete(event);
default: 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; return undefined;
} }
} }
@ -104,7 +114,10 @@ export default class TransactionHandler {
abort = true; 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) { if (transaction && !transaction.completed) {
transaction.completed = true; transaction.completed = true;
try { try {
transaction.onComplete(null, event.committed, Object.assign({}, event.snapshot)); transaction.onComplete(
null,
event.committed,
Object.assign({}, event.snapshot)
);
} finally { } finally {
setImmediate(() => { setImmediate(() => {
delete this._transactions[event.id]; delete this._transactions[event.id];

View File

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

View File

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

View File

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

View File

@ -3,11 +3,27 @@
* DocumentSnapshot representation wrapper * DocumentSnapshot representation wrapper
*/ */
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import FieldPath from './FieldPath';
import Path from './Path'; import Path from './Path';
import { isObject } from '../../utils';
import { parseNativeMap } from './utils/serialize'; import { parseNativeMap } from './utils/serialize';
import type Firestore from './'; 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 * @class DocumentSnapshot
@ -17,10 +33,16 @@ export default class DocumentSnapshot {
_metadata: FirestoreSnapshotMetadata; _metadata: FirestoreSnapshotMetadata;
_ref: DocumentReference; _ref: DocumentReference;
constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentSnapshot) { constructor(
firestore: Firestore,
nativeData: FirestoreNativeDocumentSnapshot
) {
this._data = parseNativeMap(firestore, nativeData.data); this._data = parseNativeMap(firestore, nativeData.data);
this._metadata = nativeData.metadata; this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path)); this._ref = new DocumentReference(
firestore,
Path.fromName(nativeData.path)
);
} }
get exists(): boolean { get exists(): boolean {
@ -43,7 +65,10 @@ export default class DocumentSnapshot {
return this._data; 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; 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 * @package
*/ */
static fromName(name): Path { static fromName(name: string): Path {
const parts = name.split('/'); const parts = name.split('/');
if (parts.length === 0) { if (parts.length === 0) {

View File

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

View File

@ -2,6 +2,8 @@
* @flow * @flow
* WriteBatch representation wrapper * WriteBatch representation wrapper
*/ */
import FieldPath from './FieldPath';
import { mergeFieldPathData } from './utils';
import { buildNativeMap } from './utils/serialize'; import { buildNativeMap } from './utils/serialize';
import { isObject, isString } from '../../utils'; import { isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native'; import { getNativeModule } from '../../utils/native';
@ -15,7 +17,7 @@ type DocumentWrite = {
options?: Object, options?: Object,
path: string, path: string,
type: 'DELETE' | 'SET' | 'UPDATE', type: 'DELETE' | 'SET' | 'UPDATE',
} };
/** /**
* @class WriteBatch * @class WriteBatch
@ -45,7 +47,11 @@ export default class WriteBatch {
return this; return this;
} }
set(docRef: DocumentReference, data: Object, writeOptions?: FirestoreWriteOptions) { set(
docRef: DocumentReference,
data: Object,
writeOptions?: FirestoreWriteOptions
) {
// TODO: Validation // TODO: Validation
// validate.isDocumentReference('docRef', docRef); // validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data); // validate.isDocument('data', data);
@ -67,19 +73,29 @@ export default class WriteBatch {
let data = {}; let data = {};
if (args.length === 1) { if (args.length === 1) {
if (!isObject(args[0])) { 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]; data = args[0];
} else if (args.length % 2 === 1) { } 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 { } else {
for (let i = 0; i < args.length; i += 2) { for (let i = 0; i < args.length; i += 2) {
const key = args[i]; const key = args[i];
const value = args[i + 1]; const value = args[i + 1];
if (!isString(key)) { if (isString(key)) {
throw new Error(`DocumentReference.update failed: Argument at index ${i + 1} must be a string`); 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 ModuleBase from '../../utils/ModuleBase';
import CollectionReference from './CollectionReference'; import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import FieldPath from './FieldPath';
import FieldValue from './FieldValue'; import FieldValue from './FieldValue';
import GeoPoint from './GeoPoint'; import GeoPoint from './GeoPoint';
import Path from './Path'; import Path from './Path';
@ -24,7 +25,7 @@ type CollectionSyncEvent = {
error?: Object, error?: Object,
listenerId: string, listenerId: string,
path: string, path: string,
} };
type DocumentSyncEvent = { type DocumentSyncEvent = {
appName: string, appName: string,
@ -32,7 +33,7 @@ type DocumentSyncEvent = {
error?: Object, error?: Object,
listenerId: string, listenerId: string,
path: string, path: string,
} };
const NATIVE_EVENTS = [ const NATIVE_EVENTS = [
'firestore_collection_sync_event', 'firestore_collection_sync_event',
@ -52,6 +53,7 @@ export default class Firestore extends ModuleBase {
super(app, { super(app, {
events: NATIVE_EVENTS, events: NATIVE_EVENTS,
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
this._referencePath = new Path([]); this._referencePath = new Path([]);
@ -60,14 +62,14 @@ export default class Firestore extends ModuleBase {
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onCollectionSnapshot // public event name: onCollectionSnapshot
getAppEventName(this, 'firestore_collection_sync_event'), getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this), this._onCollectionSyncEvent.bind(this)
); );
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onDocumentSnapshot // public event name: onDocumentSnapshot
getAppEventName(this, 'firestore_document_sync_event'), 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 { 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 { settings(): void {
@ -126,9 +133,15 @@ export default class Firestore extends ModuleBase {
*/ */
_onCollectionSyncEvent(event: CollectionSyncEvent) { _onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) { if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error); SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
event.error
);
} else { } 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) { _onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) { if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error); SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
event.error
);
} else { } else {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot); SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`),
event.documentSnapshot
);
} }
} }
} }
export const statics = { export const statics = {
FieldPath,
FieldValue, FieldValue,
GeoPoint, GeoPoint,
enableLogging(enabled: boolean) { 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 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 GeoPoint from '../GeoPoint';
import Path from '../Path'; import Path from '../Path';
import { typeOf } from '../../../utils'; import { typeOf } from '../../../utils';
@ -17,10 +21,12 @@ import type { FirestoreTypeMap } from '../../../types';
* for transmission to the native side * for transmission to the native side
*/ */
export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } => { export const buildNativeMap = (
data: Object
): { [string]: FirestoreTypeMap } => {
const nativeData = {}; const nativeData = {};
if (data) { if (data) {
Object.keys(data).forEach((key) => { Object.keys(data).forEach(key => {
const typeMap = buildTypeMap(data[key]); const typeMap = buildTypeMap(data[key]);
if (typeMap) { if (typeMap) {
nativeData[key] = typeMap; nativeData[key] = typeMap;
@ -33,7 +39,7 @@ export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } =>
export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => { export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => {
const nativeArray = []; const nativeArray = [];
if (array) { if (array) {
array.forEach((value) => { array.forEach(value => {
const typeMap = buildTypeMap(value); const typeMap = buildTypeMap(value);
if (typeMap) { if (typeMap) {
nativeArray.push(typeMap); nativeArray.push(typeMap);
@ -60,6 +66,11 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
type: 'fieldvalue', type: 'fieldvalue',
value: 'timestamp', value: 'timestamp',
}; };
} else if (value === DOCUMENT_ID) {
return {
type: 'documentid',
value: null,
};
} else if (type === 'boolean' || type === 'number' || type === 'string') { } else if (type === 'boolean' || type === 'number' || type === 'string') {
return { return {
type, type,
@ -104,21 +115,27 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
* side and converts to the correct Firestore JS types * 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; let data;
if (nativeData) { if (nativeData) {
data = {}; data = {};
Object.keys(nativeData).forEach((key) => { Object.keys(nativeData).forEach(key => {
data[key] = parseTypeMap(firestore, nativeData[key]); data[key] = parseTypeMap(firestore, nativeData[key]);
}); });
} }
return data; return data;
}; };
const parseNativeArray = (firestore: Firestore, nativeArray: FirestoreTypeMap[]): any[] => { const parseNativeArray = (
firestore: Firestore,
nativeArray: FirestoreTypeMap[]
): any[] => {
const array = []; const array = [];
if (nativeArray) { if (nativeArray) {
nativeArray.forEach((typeMap) => { nativeArray.forEach(typeMap => {
array.push(parseTypeMap(firestore, typeMap)); array.push(parseTypeMap(firestore, typeMap));
}); });
} }

View File

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

View File

@ -12,7 +12,9 @@ export default class RemoteMessage {
id: generatePushID(), id: generatePushID(),
ttl: 3600, ttl: 3600,
// add the googleapis sender id part if not already added. // 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', type: 'remote',
data: {}, data: {},
}; };
@ -57,7 +59,6 @@ export default class RemoteMessage {
return this; return this;
} }
/** /**
* *
* @param data * @param data
@ -65,7 +66,9 @@ export default class RemoteMessage {
*/ */
setData(data: Object = {}) { setData(data: Object = {}) {
if (!isObject(data)) { 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); const props = Object.keys(data);

View File

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

View File

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

View File

@ -14,10 +14,7 @@ import type App from '../core/firebase-app';
const FirebaseStorage = NativeModules.RNFirebaseStorage; const FirebaseStorage = NativeModules.RNFirebaseStorage;
const NATIVE_EVENTS = [ const NATIVE_EVENTS = ['storage_event', 'storage_error'];
'storage_event',
'storage_error',
];
export const MODULE_NAME = 'RNFirebaseStorage'; export const MODULE_NAME = 'RNFirebaseStorage';
export const NAMESPACE = 'storage'; export const NAMESPACE = 'storage';
@ -32,17 +29,18 @@ export default class Storage extends ModuleBase {
super(app, { super(app, {
events: NATIVE_EVENTS, events: NATIVE_EVENTS,
moduleName: MODULE_NAME, moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
getAppEventName(this, 'storage_event'), getAppEventName(this, 'storage_event'),
this._handleStorageEvent.bind(this), this._handleStorageEvent.bind(this)
); );
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
getAppEventName(this, 'storage_error'), 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); 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); SharedEventEmitter.addListener(this._getSubEventName(path, eventName), cb);
} }
_removeListener(path: string, eventName: string, origCB: (evt: Object) => Object): void { _removeListener(
SharedEventEmitter.removeListener(this._getSubEventName(path, eventName), origCB); 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', CANCELLED: 'cancelled',
ERROR: 'error', ERROR: 'error',
}, },
Native: FirebaseStorage ? { Native: FirebaseStorage
MAIN_BUNDLE_PATH: FirebaseStorage.MAIN_BUNDLE_PATH, ? {
CACHES_DIRECTORY_PATH: FirebaseStorage.CACHES_DIRECTORY_PATH, MAIN_BUNDLE_PATH: FirebaseStorage.MAIN_BUNDLE_PATH,
DOCUMENT_DIRECTORY_PATH: FirebaseStorage.DOCUMENT_DIRECTORY_PATH, CACHES_DIRECTORY_PATH: FirebaseStorage.CACHES_DIRECTORY_PATH,
EXTERNAL_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_DIRECTORY_PATH, DOCUMENT_DIRECTORY_PATH: FirebaseStorage.DOCUMENT_DIRECTORY_PATH,
EXTERNAL_STORAGE_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_STORAGE_DIRECTORY_PATH, EXTERNAL_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_DIRECTORY_PATH,
TEMP_DIRECTORY_PATH: FirebaseStorage.TEMP_DIRECTORY_PATH, EXTERNAL_STORAGE_DIRECTORY_PATH:
LIBRARY_DIRECTORY_PATH: FirebaseStorage.LIBRARY_DIRECTORY_PATH, FirebaseStorage.EXTERNAL_STORAGE_DIRECTORY_PATH,
FILETYPE_REGULAR: FirebaseStorage.FILETYPE_REGULAR, TEMP_DIRECTORY_PATH: FirebaseStorage.TEMP_DIRECTORY_PATH,
FILETYPE_DIRECTORY: FirebaseStorage.FILETYPE_DIRECTORY, 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 { getNativeModule } from '../../utils/native';
import type Storage from './'; import type Storage from './';
/** /**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference * @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
*/ */
@ -75,7 +74,11 @@ export default class StorageReference extends ReferenceBase {
* @return {Promise} * @return {Promise}
*/ */
downloadFile(filePath: string): Promise<Object> { downloadFile(filePath: string): Promise<Object> {
return new StorageTask(DOWNLOAD_TASK, 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> { putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
const _filePath = filePath.replace('file://', ''); 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 = { declare type UploadTaskSnapshotType = {
bytesTransferred: number, 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 metadata: Object, // TODO flow type def for https://firebase.google.com/docs/reference/js/firebase.storage.FullMetadata.html
ref: StorageReference, ref: StorageReference,
state: ( state:
typeof StorageStatics.TaskState.RUNNING | typeof StorageStatics.TaskState.RUNNING
| typeof StorageStatics.TaskState.PAUSED | typeof StorageStatics.TaskState.PAUSED
| typeof StorageStatics.TaskState.SUCCESS | typeof StorageStatics.TaskState.SUCCESS
| typeof StorageStatics.TaskState.CANCELLED | typeof StorageStatics.TaskState.CANCELLED
| typeof StorageStatics.TaskState.ERROR | typeof StorageStatics.TaskState.ERROR,
),
task: StorageTask, task: StorageTask,
totalBytes: number, 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 | declare type NextOrObserverType =
{ | null
next?: FuncSnapshotType, | {
error?: FuncErrorType, next?: FuncSnapshotType,
complete?:FuncSnapshotType error?: FuncErrorType,
} | complete?: FuncSnapshotType,
FuncSnapshotType; }
| FuncSnapshotType;
/** /**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask * @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask
@ -49,7 +51,11 @@ export default class StorageTask {
then: () => Promise<*>; then: () => Promise<*>;
catch: () => Promise<*>; catch: () => Promise<*>;
constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) { constructor(
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK,
promise: Promise<*>,
storageRef: StorageReference
) {
this.type = type; this.type = type;
this.ref = storageRef; this.ref = storageRef;
this.storage = storageRef._storage; this.storage = storageRef._storage;
@ -66,9 +72,9 @@ export default class StorageTask {
* @returns {Promise.<T>} * @returns {Promise.<T>}
* @private * @private
*/ */
_interceptSnapshotEvent(f: ?Function): null | () => * { _interceptSnapshotEvent(f: ?Function): null | (() => *) {
if (!isFunction(f)) return null; if (!isFunction(f)) return null;
return (snapshot) => { return snapshot => {
const _snapshot = Object.assign({}, snapshot); const _snapshot = Object.assign({}, snapshot);
_snapshot.task = this; _snapshot.task = this;
_snapshot.ref = this.ref; _snapshot.ref = this.ref;
@ -82,9 +88,9 @@ export default class StorageTask {
* @returns {*} * @returns {*}
* @private * @private
*/ */
_interceptErrorEvent(f: ?Function): null | (Error) => * { _interceptErrorEvent(f: ?Function): null | (Error => *) {
if (!isFunction(f)) return null; if (!isFunction(f)) return null;
return (error) => { return error => {
const _error = new Error(error.message); const _error = new Error(error.message);
// $FlowFixMe // $FlowFixMe
_error.code = error.code; _error.code = error.code;
@ -100,7 +106,11 @@ export default class StorageTask {
* @returns {function()} * @returns {function()}
* @private * @private
*/ */
_subscribe(nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): Function { _subscribe(
nextOrObserver: NextOrObserverType,
error: FuncErrorType,
complete: FuncSnapshotType
): Function {
let _error; let _error;
let _next; let _next;
let _complete; let _complete;
@ -119,28 +129,31 @@ export default class StorageTask {
this.storage._addListener( this.storage._addListener(
this.path, this.path,
StorageStatics.TaskEvent.STATE_CHANGED, StorageStatics.TaskEvent.STATE_CHANGED,
_next, _next
); );
} }
if (_error) { if (_error) {
this.storage._addListener( this.storage._addListener(this.path, `${this.type}_failure`, _error);
this.path,
`${this.type}_failure`,
_error,
);
} }
if (_complete) { if (_complete) {
this.storage._addListener( this.storage._addListener(this.path, `${this.type}_success`, _complete);
this.path,
`${this.type}_success`,
_complete,
);
} }
return () => { return () => {
if (_next) this.storage._removeListener(this.path, StorageStatics.TaskEvent.STATE_CHANGED, _next); if (_next)
if (_error) this.storage._removeListener(this.path, `${this.type}_failure`, _error); this.storage._removeListener(
if (_complete) this.storage._removeListener(this.path, `${this.type}_success`, _complete); 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 * @param complete
* @returns {function()} * @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) { 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) { 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 // if only event provided return the subscriber function
@ -170,16 +194,22 @@ export default class StorageTask {
} }
pause() { 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() { resume() {
// todo // 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() { cancel() {
// todo // 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 // @flow
import { NativeModules } from 'react-native'; 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 INTERNALS from '../../utils/internals';
import { isIOS } from '../../utils'; import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase'; import ModuleBase from '../../utils/ModuleBase';
import PACKAGE from '../../../package.json'; import type App from '../core/firebase-app';
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
@ -14,13 +12,21 @@ type GoogleApiAvailabilityType = {
isAvailable: boolean, isAvailable: boolean,
isUserResolvableError?: boolean, isUserResolvableError?: boolean,
hasResolution?: boolean, hasResolution?: boolean,
error?: string error?: string,
} };
export const MODULE_NAME = 'RNFirebaseUtils'; export const MODULE_NAME = 'RNFirebaseUtils';
export const NAMESPACE = 'utils'; export const NAMESPACE = 'utils';
export default class RNFirebaseUtils extends ModuleBase { 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; const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) { if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) { if (
INTERNALS.OPTIONS.promptOnMissingPlayServices &&
this.playServicesAvailability.isUserResolvableError
) {
this.promptForPlayServices(); this.promptForPlayServices();
} else { } else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status); const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) { 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 throw new Error(error);
} else { } else {
console.warn(error); console.warn(error);
@ -74,7 +84,12 @@ export default class RNFirebaseUtils extends ModuleBase {
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}} * @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
*/ */
get playServicesAvailability(): GoogleApiAvailabilityType { 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 = {};
export const statics = {
// VERSIONS: {
// react: ReactVersion,
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),
// 'react-native-firebase': PACKAGE.version,
// },
};

View File

@ -35,24 +35,46 @@ export type FirebaseError = {
stack: string, stack: string,
path: string, path: string,
details: string, details: string,
modifiers: string modifiers: string,
} };
export type FirebaseModule = $Subtype<ModuleBase>; export type FirebaseModule = $Subtype<ModuleBase>;
export type FirebaseModuleConfig = { export type FirebaseModuleConfig = {
events?: string[], events?: string[],
moduleName: FirebaseModuleName, moduleName: FirebaseModuleName,
multiApp: boolean,
namespace: FirebaseNamespace, namespace: FirebaseNamespace,
} };
export type FirebaseModuleName = 'RNFirebaseAdmob' | 'RNFirebaseAnalytics' | 'RNFirebaseAuth' export type FirebaseModuleName =
| 'RNFirebaseRemoteConfig' | 'RNFirebaseCrash' | 'RNFirebaseCrashlytics' | 'RNFirebaseDatabase' | 'RNFirebaseAdMob'
| 'RNFirebaseFirestore' | 'RNFirebaseLinks' | 'RNFirebaseMessaging' | 'RNFirebasePerformance' | 'RNFirebaseAnalytics'
| 'RNFirebaseStorage' | 'RNFirebaseUtils'; | 'RNFirebaseAuth'
| 'RNFirebaseRemoteConfig'
| 'RNFirebaseCrash'
| 'RNFirebaseCrashlytics'
| 'RNFirebaseDatabase'
| 'RNFirebaseFirestore'
| 'RNFirebaseLinks'
| 'RNFirebaseMessaging'
| 'RNFirebasePerformance'
| 'RNFirebaseStorage'
| 'RNFirebaseUtils';
export type FirebaseNamespace = 'admob' | 'analytics' | 'auth' | 'config' | 'crash' export type FirebaseNamespace =
| 'crashlytics' | 'database' | 'firestore' | 'links' | 'messaging' | 'perf' | 'storage' | 'admob'
| 'analytics'
| 'auth'
| 'config'
| 'crash'
| 'crashlytics'
| 'database'
| 'firestore'
| 'links'
| 'messaging'
| 'perf'
| 'storage'
| 'utils'; | 'utils';
export type FirebaseOptions = { export type FirebaseOptions = {
@ -62,7 +84,7 @@ export type FirebaseOptions = {
messagingSenderId: string, messagingSenderId: string,
projectId: string, projectId: string,
storageBucket: string, storageBucket: string,
} };
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = { export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {
(): M, (): M,
@ -92,12 +114,6 @@ export type ConfigModule = {
nativeModuleExists: boolean, nativeModuleExists: boolean,
} & ConfigStatics; } & ConfigStatics;
export type AuthCredential = {
providerId: string,
token: string,
secret: string
}
/* Auth types */ /* Auth types */
export type AuthModule = { export type AuthModule = {
@ -105,19 +121,6 @@ export type AuthModule = {
nativeModuleExists: boolean, nativeModuleExists: boolean,
} & AuthStatics; } & AuthStatics;
export type ActionCodeSettings = {
android: {
installApp?: boolean,
minimumVersion?: string,
packageName: string,
},
handleCodeInApp?: boolean,
iOS: {
bundleId?: string,
},
url: string,
}
/* Crash types */ /* Crash types */
export type CrashModule = { export type CrashModule = {
@ -133,14 +136,14 @@ export type DatabaseModule = {
} & DatabaseStatics; } & DatabaseStatics;
export type DatabaseModifier = { export type DatabaseModifier = {
id: string; id: string,
type: 'orderBy' | 'limit' | 'filter'; type: 'orderBy' | 'limit' | 'filter',
name?: string; name?: string,
key?: string; key?: string,
limit?: number; limit?: number,
value?: any; value?: any,
valueType?: string; valueType?: string,
} };
/* Fabric types */ /* Fabric types */
export type CrashlyticsModule = { export type CrashlyticsModule = {
@ -150,7 +153,7 @@ export type CrashlyticsModule = {
export type FabricModule = { export type FabricModule = {
crashlytics: CrashlyticsModule, crashlytics: CrashlyticsModule,
} };
/* Firestore types */ /* Firestore types */
@ -164,30 +167,41 @@ export type FirestoreNativeDocumentChange = {
newIndex: number, newIndex: number,
oldIndex: number, oldIndex: number,
type: string, type: string,
} };
export type FirestoreNativeDocumentSnapshot = { export type FirestoreNativeDocumentSnapshot = {
data: { [string]: FirestoreTypeMap }, data: { [string]: FirestoreTypeMap },
metadata: FirestoreSnapshotMetadata, metadata: FirestoreSnapshotMetadata,
path: string, path: string,
} };
export type FirestoreSnapshotMetadata = { export type FirestoreSnapshotMetadata = {
fromCache: boolean, fromCache: boolean,
hasPendingWrites: boolean, hasPendingWrites: boolean,
} };
export type FirestoreQueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc'; export type FirestoreQueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
export type FirestoreQueryOperator = '<' | '<=' | '=' | '==' | '>' | '>='; export type FirestoreQueryOperator = '<' | '<=' | '=' | '==' | '>' | '>=';
export type FirestoreTypeMap = { 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, value: any,
} };
export type FirestoreWriteOptions = { export type FirestoreWriteOptions = {
merge?: boolean, merge?: boolean,
} };
/* Links types */ /* Links types */

View File

@ -29,7 +29,10 @@ export default class ModuleBase {
// check if native module exists as all native // check if native module exists as all native
initialiseNativeModule(this, config); 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; path: string;
constructor(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} * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key}
*/ */
get key(): string | null { get key(): string | null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1); return this.path === '/'
? null
: this.path.substring(this.path.lastIndexOf('/') + 1);
} }
} }

View File

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

View File

@ -18,7 +18,7 @@ import type {
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {}; const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule }} = {}; const APP_MODULES: { [App]: { [string]: FirebaseModule } } = {};
const DEFAULT_APP_NAME = '[DEFAULT]'; const DEFAULT_APP_NAME = '[DEFAULT]';
export default { export default {
@ -43,15 +43,23 @@ export default {
* @return {function()} * @return {function()}
* @private * @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 => { return (): M => {
if (!APP_MODULES[app]) { if (!APP_MODULES[app]) {
APP_MODULES[app] = {}; APP_MODULES[app] = {};
} }
if (isAndroid && namespace !== 'utils' && !INTERNALS.FLAGS.checkedPlayServices) { if (
isAndroid &&
namespace !== 'utils' &&
!INTERNALS.FLAGS.checkedPlayServices
) {
INTERNALS.FLAGS.checkedPlayServices = true; INTERNALS.FLAGS.checkedPlayServices = true;
this.utils().checkPlayServicesAvailability(); app.utils().checkPlayServicesAvailability();
} }
if (!APP_MODULES[app][namespace]) { if (!APP_MODULES[app][namespace]) {
@ -148,16 +156,21 @@ export default {
* @param InstanceClass * @param InstanceClass
* @return {function(App=)} * @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 => { const getModule = (app?: App): FirebaseModule => {
let _app = app; let _app = app;
// throw an error if it's not a valid app instance // 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)); 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 else if (!_app)
// if default app not initialized // default to the 'DEFAULT' app if no arg provided - will throw an error
else if (!_app) _app = this.app(DEFAULT_APP_NAME); // if default app not initialized
_app = this.app(DEFAULT_APP_NAME);
if (namespace === 'crashlytics') { if (namespace === 'crashlytics') {
return _app.fabric[namespace](); return _app.fabric[namespace]();
} }

View File

@ -12,11 +12,15 @@ const NATIVE_SUBSCRIPTIONS: { [string]: boolean } = {};
export const SharedEventEmitter = new EventEmitter(); export const SharedEventEmitter = new EventEmitter();
export const getAppEventName = (module: ModuleBase, eventName: string): string => { export const getAppEventName = (
return `${module.app.name}-${eventName}`; 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 name = `${module.app.name}-${moduleName}`;
const nativeModule = NativeModules[moduleName]; const nativeModule = NativeModules[moduleName];
if (!NATIVE_EMITTERS[name]) { if (!NATIVE_EMITTERS[name]) {
@ -35,10 +39,14 @@ const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): N
* @param eventName * @param eventName
* @private * @private
*/ */
const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: ModuleBase, eventName: string): void => { const subscribeToNativeModuleEvents = (
moduleName: FirebaseModuleName,
module: ModuleBase,
eventName: string
): void => {
if (!NATIVE_SUBSCRIPTIONS[eventName]) { if (!NATIVE_SUBSCRIPTIONS[eventName]) {
const nativeEmitter = getNativeEmitter(moduleName, module); const nativeEmitter = getNativeEmitter(moduleName, module);
nativeEmitter.addListener(eventName, (event) => { nativeEmitter.addListener(eventName, event => {
if (event.appName) { if (event.appName) {
// native event has an appName property - auto prefix and internally emit // native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event); 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; const { events, moduleName } = config;
if (events && events.length) { if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) { 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 // todo cleanup unused utilities from legacy code
// modeled after base64 web-safe chars, but ordered by ASCII // modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; const PUSH_CHARS =
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const { hasOwnProperty } = Object; const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50; // const DEFAULT_CHUNK_SIZE = 50;
@ -18,7 +20,11 @@ const { hasOwnProperty } = Object;
* @param joiner * @param joiner
* @returns {*} * @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); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -42,7 +48,11 @@ export function deepGet(object: Object, path: string, joiner?: string = '/'): an
* @param joiner * @param joiner
* @returns {*} * @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); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -64,18 +74,23 @@ export function deepExists(object: Object, path: string, joiner?: string = '/'):
* @param obj2 * @param obj2
* @returns {boolean} * @returns {boolean}
*/ */
export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): boolean { export function areObjectKeysContainedInOther(
obj1: Object,
obj2: Object
): boolean {
if (!isObject(obj1) || !isObject(obj2)) { if (!isObject(obj1) || !isObject(obj2)) {
return false; return false;
} }
const keys1 = Object.keys(obj1); const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2); const keys2 = Object.keys(obj2);
if (isArrayContainedInOther(keys1, keys2)) { if (isArrayContainedInOther(keys1, keys2)) {
return keys1.filter((key) => { return keys1
return isObject(obj1[key]); .filter(key => isObject(obj1[key]))
}).reduce((acc, cur) => { .reduce(
return acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]); (acc, cur) =>
}, true); acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]),
true
);
} }
return false; return false;
} }
@ -86,13 +101,14 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2 * @param arr2
* @returns {boolean} * @returns {boolean}
*/ */
export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean { export function isArrayContainedInOther(
arr1: Array<*>,
arr2: Array<*>
): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) { if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false; return false;
} }
return arr1.reduce((acc, cur) => { return arr1.reduce((acc, cur) => acc && arr2.includes(cur), true);
return acc && arr2.includes(cur);
}, true);
} }
/** /**
@ -101,7 +117,9 @@ export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean
* @returns {boolean} * @returns {boolean}
*/ */
export function isObject(item: mixed): boolean %checks { 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 isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android'; export const isAndroid = Platform.OS === 'android';
/** /**
* *
* @param string * @param string
@ -153,16 +170,16 @@ export function tryJSONStringify(data: mixed): string | null {
} }
} }
export const windowOrGlobal =
// noinspection Eslint // eslint-disable-next-line no-restricted-globals
export const windowOrGlobal = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this; (typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this;
/** /**
* No operation func * No operation func
*/ */
export function noop(): void { export function noop(): void {}
}
// /** // /**
// * Delays chunks based on sizes per event loop. // * Delays chunks based on sizes per event loop.
@ -286,7 +303,7 @@ const lastRandChars = [];
export function generatePushID(serverTimeOffset?: number = 0): string { export function generatePushID(serverTimeOffset?: number = 0): string {
const timeStampChars = new Array(8); const timeStampChars = new Array(8);
let now = new Date().getTime() + serverTimeOffset; let now = new Date().getTime() + serverTimeOffset;
const duplicateTime = (now === lastPushTime); const duplicateTime = now === lastPushTime;
lastPushTime = now; lastPushTime = now;
@ -295,7 +312,8 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
now = Math.floor(now / 64); 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(''); let id = timeStampChars.join('');
@ -330,7 +348,11 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
* @param additionalProps * @param additionalProps
* @returns {Error} * @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); const error: Object = new Error(message);
error.code = code; error.code = code;
Object.assign(error, additionalProps); Object.assign(error, additionalProps);
@ -362,7 +384,6 @@ export function objectToUniqueId(object: Object): string {
return key; return key;
} }
/** /**
* Return the existing promise if no callback provided or * Return the existing promise if no callback provided or
* exec the promise and callback if optionalCallback is valid. * exec the promise and callback if optionalCallback is valid.
@ -371,23 +392,28 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback * @param optionalCallback
* @return {Promise} * @return {Promise}
*/ */
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> { export function promiseOrCallback(
promise: Promise<*>,
optionalCallback?: Function
): Promise<*> {
if (!isFunction(optionalCallback)) return promise; if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => { return promise
// some of firebase internal tests & methods only check/return one arg .then(result => {
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62 // some of firebase internal tests & methods only check/return one arg
if (optionalCallback && optionalCallback.length === 1) { // see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
optionalCallback(null); if (optionalCallback && optionalCallback.length === 1) {
} else if (optionalCallback) { optionalCallback(null);
optionalCallback(null, result); } else if (optionalCallback) {
} optionalCallback(null, result);
}
return Promise.resolve(result); return Promise.resolve(result);
}).catch((error) => { })
if (optionalCallback) optionalCallback(error); .catch(error => {
return Promise.reject(error); if (optionalCallback) optionalCallback(error);
}); return Promise.reject(error);
});
} }
/** /**
@ -398,7 +424,9 @@ export function firestoreAutoId(): string {
let autoId = ''; let autoId = '';
for (let i = 0; i < 20; i++) { 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; 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 // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: { 2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED', code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.', message:
'The installed version of Google Play services on this device is out of date.',
}, },
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
3: { 3: {
code: 'SERVICE_DISABLED', code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.', message:
'The installed version of Google Play services has been disabled on this device.',
}, },
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
9: { 9: {
code: 'SERVICE_INVALID', code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.', message:
'The version of the Google Play services installed on this device is not authentic.',
}, },
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
18: { 18: {
@ -49,7 +52,8 @@ const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
19: { 19: {
code: 'SERVICE_MISSING_PERMISSION', code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.', message:
"Google Play service doesn't have one or more required permissions.",
}, },
}; };
@ -66,27 +70,33 @@ export default {
}, },
STRINGS: { STRINGS: {
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' + WARN_INITIALIZE_DEPRECATION:
'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.', "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} * @return {string}
*/ */
get ERROR_MISSING_CORE() { get ERROR_MISSING_CORE() {
if (Platform.OS === 'ios') { 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`.' + '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.' + '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:
ERROR_INIT_OBJECT: 'Firebase.initializeApp(options <-- requires a valid configuration object.', 'Firebase.initializeApp(options <-- requires a valid configuration object.',
ERROR_INIT_STRING_NAME: 'Firebase.initializeApp(options, name <-- requires a valid string value.', ERROR_INIT_STRING_NAME:
'Firebase.initializeApp(options, name <-- requires a valid string value.',
/** /**
* @return {string} * @return {string}
@ -131,20 +141,28 @@ export default {
ERROR_MISSING_MODULE(namespace: string, nativeModule: string) { ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
const snippet = `firebase.${namespace}()`; const snippet = `firebase.${namespace}()`;
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` + return (
`You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
'\r\n\r\nEnsure you have the required Firebase iOS SDK pod for this module included in your Podfile, in this instance ' + '\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` + `confirm you've added "pod '${
'\r\n\r\nSee http://invertase.link/ios for full setup instructions.'; 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 rnFirebasePackage = `'io.invertase.firebase.${namespace}.${nativeModule}Package'`;
const newInstance = `'new ${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' ` + `\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 ` + `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.` + `${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 `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_PLAY_SERVICES(statusCode: number) { ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode]; const knownError = PLAY_SERVICES_CODES[statusCode];
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.'; let start =
'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
if (statusCode === 2) { 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 // eslint-disable-next-line prefer-template
return `${start}\r\n\r\n` + return (
'-------------------------\r\n' + `${`${start}\r\n\r\n-------------------------\r\n`}${
(knownError ? knownError
`${knownError.code}: ${knownError.message} (code ${statusCode})` : ? `${knownError.code}: ${knownError.message} (code ${statusCode})`
`A specific play store availability reason reason was not available (unknown code: ${statusCode})` : `A specific play store availability reason reason was not available (unknown code: ${statusCode})`
) + }\r\n-------------------------` +
'\r\n-------------------------' + `\r\n\r\n` +
'\r\n\r\n' + `For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:` +
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' + `\r\n\r\nhttp://invertase.link/play-services`
'\r\n\r\nhttp://invertase.link/play-services'; );
}, },
}, },
}; };

View File

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

View File

@ -8,14 +8,6 @@ import INTERNALS from './internals';
import type ModuleBase from './ModuleBase'; import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig } from '../types'; 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 } = {}; 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++) { for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i]; const method = methods[i];
native[method] = (...args) => { native[method] = (...args) => NativeModule[method](...[appName, ...args]);
return NativeModule[method](...[appName, ...args]);
};
} }
return native; 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 => { export const getNativeModule = (module: ModuleBase): Object => {
const key = getModuleKey(module); const key = getModuleKey(module);
return NATIVE_MODULES[key]; return NATIVE_MODULES[key];
}; };
export const initialiseNativeModule = (module: ModuleBase, config: FirebaseModuleConfig): Object => { export const initialiseNativeModule = (
const { moduleName, namespace } = config; module: ModuleBase,
config: FirebaseModuleConfig
): Object => {
const { moduleName, multiApp, namespace } = config;
const nativeModule = NativeModules[moduleName]; const nativeModule = NativeModules[moduleName];
const key = getModuleKey(module); const key = getModuleKey(module);
if (!nativeModule && namespace !== 'utils') { 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 // used by the modules that extend ModuleBase
// to access their native module counterpart // to access their native module counterpart
if (!MULTI_APP_MODULES.includes(moduleName)) { if (multiApp) {
NATIVE_MODULES[key] = nativeModule;
} else {
NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule); NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule);
} else {
NATIVE_MODULES[key] = nativeModule;
} }
initialiseNativeModuleEventEmitter(module, config); 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", "name": "react-native-firebase",
"version": "3.2.0", "version": "3.2.4",
"author": "Invertase <contact@invertase.io> (http://invertase.io)", "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.", "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", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"scripts": { "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-flow": "flow-copy-source -i */__tests__* lib dist",
"build-lib": "BABEL_ENV=publish babel lib -d dist --ignore __tests__ --copy-files", "build-lib": "BABEL_ENV=publish babel lib -d dist --ignore __tests__ --copy-files",
"clean": "rimraf dist/", "clean": "rimraf dist/",
"flow": "flow", "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", "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", "test-cli": "node ./bin/test.js",
"tests-packager": "cd tests && npm run start", "tests-packager": "cd tests && npm run start",
"tests-npm-install": "cd tests && npm install", "tests-npm-install": "cd tests && npm install",
"tests-pod-install": "cd tests && npm run ios:pod:install", "tests-pod-install": "cd tests && npm run ios:pod:install",
"tests-watch-start": "npm run test-cli watch init start", "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": { "repository": {
"type": "git", "type": "git",
@ -28,45 +32,10 @@
"jest": { "jest": {
"preset": "jest-react-native", "preset": "jest-react-native",
"setupFiles": [], "setupFiles": [],
"unmockedModulePathPatterns": [ "unmockedModulePathPatterns": ["./node_modules/react", "./node_modules/react-native", "./node_modules/react-native-mock", "./node_modules/react-addons-test-utils"]
"./node_modules/react",
"./node_modules/react-native",
"./node_modules/react-native-mock",
"./node_modules/react-addons-test-utils"
]
}, },
"license": "APACHE-2.0", "license": "APACHE-2.0",
"keywords": [ "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"],
"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": { "peerDependencies": {
"react": "*", "react": "*",
"react-native": ">= 0.48.0", "react-native": ">= 0.48.0",
@ -82,17 +51,24 @@
"enzyme": "^2.4.1", "enzyme": "^2.4.1",
"eslint": "^4.11.0", "eslint": "^4.11.0",
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-flowtype": "^2.39.1", "eslint-plugin-flowtype": "^2.39.1",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.4.0", "eslint-plugin-react": "^7.4.0",
"flow-bin": "^0.56.0", "flow-bin": "^0.61.0",
"flow-copy-source": "^1.2.1", "flow-copy-source": "^1.2.1",
"react": "^16.0.0", "genversion": "^2.0.1",
"react-dom": "^16.0.0", "husky": "^0.14.3",
"react-native": "^0.50.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", "rimraf": "^2.6.2",
"shelljs": "^0.7.8", "shelljs": "^0.7.8",
"typescript": "^2.6.2",
"wml": "0.0.82" "wml": "0.0.82"
}, },
"dependencies": { "dependencies": {
@ -115,5 +91,10 @@
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/react-native-firebase", "url": "https://opencollective.com/react-native-firebase",
"logo": "https://opencollective.com/opencollective/logo.txt" "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", "parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [ "plugins": [
"flowtype" "flowtype",
"prettier"
], ],
"env": { "env": {
"es6": true, "es6": true,
"jasmine": true "jasmine": true
}, },
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": { "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, "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, "no-console": 0,
"global-require": 0, "no-plusplus": 0,
"import/extensions": 0, "no-undef": 0,
"import/no-unresolved": 0, "no-underscore-dangle": "off",
"import/no-extraneous-dependencies": 0, "no-use-before-define": 0
"react/jsx-filename-extension": 0
}, },
"globals": { "globals": {
"__DEV__": true, "__DEV__": true,
"window": true, "window": true
"fetch": true,
} }
} }

View File

@ -12,36 +12,45 @@
; For RN Apps installed via npm, "Libraries" folder is inside ; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root ; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js .*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
[include] [include]
[libs] [libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow node_modules/react-native/flow/
flow/ node_modules/react-native/flow-github/
[options] [options]
emoji=true emoji=true
module.system=haste module.system=haste
experimental.strict_type_args=true
munge_underscores=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=$FlowIssue
suppress_type=$FlowFixMe 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\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(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\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true unsafe.enable_getters_and_setters=true
[version] [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 learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck: # To run your application with Buck:
# - install Buck # - install Buck
@ -11,8 +9,9 @@ import re
# #
lib_deps = [] lib_deps = []
for jarfile in glob(['libs/*.jar']): 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) lib_deps.append(':' + name)
prebuilt_jar( prebuilt_jar(
name = name, name = name,
@ -20,7 +19,7 @@ for jarfile in glob(['libs/*.jar']):
) )
for aarfile in glob(['libs/*.aar']): 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) lib_deps.append(':' + name)
android_prebuilt_aar( android_prebuilt_aar(
name = name, name = name,
@ -28,39 +27,39 @@ for aarfile in glob(['libs/*.aar']):
) )
android_library( android_library(
name = 'all-libs', name = "all-libs",
exported_deps = lib_deps exported_deps = lib_deps,
) )
android_library( android_library(
name = 'app-code', name = "app-code",
srcs = glob([ srcs = glob([
'src/main/java/**/*.java', "src/main/java/**/*.java",
]), ]),
deps = [ deps = [
':all-libs', ":all-libs",
':build_config', ":build_config",
':res', ":res",
], ],
) )
android_build_config( android_build_config(
name = 'build_config', name = "build_config",
package = 'com.reactnativefirebasedemo', package = "com.react-native-firebase-tests",
) )
android_resource( android_resource(
name = 'res', name = "res",
res = 'src/main/res', package = "com.react-native-firebase-tests",
package = 'com.reactnativefirebasedemo', res = "src/main/res",
) )
android_binary( android_binary(
name = 'app', name = "app",
package_type = 'debug', keystore = "//android/keystores:debug",
manifest = 'src/main/AndroidManifest.xml', manifest = "src/main/AndroidManifest.xml",
keystore = '//android/keystores:debug', package_type = "debug",
deps = [ deps = [
':app-code', ":app-code",
], ],
) )

View File

@ -4,11 +4,78 @@ apply plugin: 'io.fabric'
import com.android.build.OutputFile 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 = [ project.ext.react = [
// whether to bundle JS and assets in staging mode entryFile: "index.js"
bundleInDebug : false,
jsBundleDirDebug : "$buildDir/intermediates/assets/debug",
nodeExecutableAndArgs: ["/usr/local/bin/node"]
] ]
apply from: "../../node_modules/react-native/react.gradle" apply from: "../../node_modules/react-native/react.gradle"
@ -63,7 +130,7 @@ android {
variant.outputs.each { output -> variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here: // 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 // 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) def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride = output.versionCodeOverride =
@ -73,14 +140,13 @@ android {
} }
} }
project.ext.firebaseVersion = '11.6.2' project.ext.firebaseVersion = '11.8.0'
dependencies { dependencies {
// compile(project(':react-native-firebase')) {
// transitive = false
// }
compile project(':react-native-vector-icons') 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 fileTree(dir: "libs", include: ["*.jar"])
compile "com.google.android.gms:play-services-base:$firebaseVersion" compile "com.google.android.gms:play-services-base:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion" compile "com.google.firebase:firebase-ads:$firebaseVersion"

View File

@ -50,6 +50,10 @@
-dontwarn com.facebook.react.** -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 # okhttp
-keepattributes Signature -keepattributes Signature

View File

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

View File

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

10
tests/android/gradlew vendored
View File

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

View File

@ -1,8 +1,8 @@
keystore( keystore(
name = 'debug', name = "debug",
store = 'debug.keystore', properties = "debug.keystore.properties",
properties = 'debug.keystore.properties', store = "debug.keystore",
visibility = [ visibility = [
'PUBLIC', "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 { AppRegistry } from 'react-native';
import bootstrap from './src/main'; import bootstrap from './src/main';
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap); AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);

View File

@ -40,6 +40,12 @@ target 'ReactNativeFirebaseDemo' do
if target.name == "React" if target.name == "React"
target.remove_from_project target.remove_from_project
end 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 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 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
[FIRApp configure]; [FIRApp configure];
NSURL *jsCodeLocation; NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactNativeFirebaseDemo" moduleName:@"ReactNativeFirebaseDemo"
initialProperties:nil initialProperties:nil
launchOptions:launchOptions]; launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

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