├── .babelrc
├── .codeclimate.yml
├── .eslintignore
├── .eslintrc.js
├── .github
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── publish.yml
│ └── verify.yml
├── .gitignore
├── .npmignore
├── .prettierrc
├── CHANGELOG.md
├── CNAME
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── PATRONS.md
├── README.md
├── SUMMARY.md
├── bin
├── api-docs-generate.js
└── api-docs-upload.js
├── book.json
├── codecov.yml
├── docs
├── FAQ.md
├── GLOSSARY.md
├── api
│ ├── README.md
│ ├── ReactReduxFirebaseContext.md
│ ├── ReactReduxFirebaseProvider.md
│ ├── ReduxFirestoreContext.md
│ ├── ReduxFirestoreProvider.md
│ ├── constants.md
│ ├── firebaseConnect.md
│ ├── firebaseInstance.md
│ ├── firestoreConnect.md
│ ├── getFirebase.md
│ ├── helpers.md
│ ├── reducer.md
│ ├── reducers.md
│ ├── useFirebase.md
│ ├── useFirebaseConnect.md
│ ├── useFirestore.md
│ ├── useFirestoreConnect.md
│ ├── withFirebase.md
│ └── withFirestore.md
├── auth.md
├── contributing.md
├── firestore.md
├── getting_started.md
├── integrations
│ ├── README.md
│ ├── react-chrome-redux.md
│ ├── react-native.md
│ ├── redux-form.md
│ ├── redux-observable.md
│ ├── redux-persist.md
│ ├── redux-saga.md
│ ├── reselect.md
│ └── thunks.md
├── populate.md
├── queries.md
├── recipes
│ ├── README.md
│ ├── actions.md
│ ├── auth.md
│ ├── populate.md
│ ├── profile.md
│ ├── roles.md
│ ├── routing.md
│ ├── ssr.md
│ ├── typescript.md
│ └── upload.md
├── roadmap.md
├── static
│ ├── BuildPhase.png
│ ├── FirebaseOverview.png
│ ├── PlistDownload.png
│ ├── RegisterApp.png
│ ├── UrlTypes.png
│ └── dataFlow.png
├── storage.md
├── v2-migration-guide.md
└── v3-migration-guide.md
├── examples
├── complete
│ ├── firestore
│ │ ├── .env
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ ├── logo192.png
│ │ │ ├── logo512.png
│ │ │ ├── manifest.json
│ │ │ └── robots.txt
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── App.js
│ │ │ ├── App.test.js
│ │ │ ├── Home.js
│ │ │ ├── NewTodo.js
│ │ │ ├── Todo.css
│ │ │ ├── TodoItem.js
│ │ │ ├── Todos.js
│ │ │ ├── Todos.test.js
│ │ │ ├── config.js
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── logo.svg
│ │ │ ├── reducer.js
│ │ │ ├── serviceWorker.js
│ │ │ ├── setupTests.js
│ │ │ └── store.js
│ │ └── yarn.lock
│ ├── material
│ │ ├── .env.local
│ │ ├── .eslintignore
│ │ ├── .eslintrc.js
│ │ ├── .firebaserc
│ │ ├── .gitignore
│ │ ├── .gitlab-ci.yml
│ │ ├── .yo-rc.json
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── firestore.indexes.json
│ │ ├── firestore.rules
│ │ ├── jsconfig.json
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── humans.txt
│ │ │ ├── index.html
│ │ │ ├── manifest.json
│ │ │ └── robots.txt
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── FormTextField
│ │ │ │ │ ├── FormTextField.js
│ │ │ │ │ └── index.js
│ │ │ │ └── LoadingSpinner
│ │ │ │ │ ├── LoadingSpinner.enhancer.js
│ │ │ │ │ ├── LoadingSpinner.js
│ │ │ │ │ ├── LoadingSpinner.styles.js
│ │ │ │ │ ├── LoadingSpinner.test.js
│ │ │ │ │ └── index.js
│ │ │ ├── config.js
│ │ │ ├── constants
│ │ │ │ ├── formNames.js
│ │ │ │ └── paths.js
│ │ │ ├── containers
│ │ │ │ ├── App
│ │ │ │ │ ├── App.js
│ │ │ │ │ └── index.js
│ │ │ │ └── Navbar
│ │ │ │ │ ├── AccountMenu.js
│ │ │ │ │ ├── LoginMenu.js
│ │ │ │ │ ├── Navbar.js
│ │ │ │ │ ├── Navbar.styles.js
│ │ │ │ │ └── index.js
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── layouts
│ │ │ │ └── CoreLayout
│ │ │ │ │ ├── CoreLayout.js
│ │ │ │ │ ├── CoreLayout.styles.js
│ │ │ │ │ └── index.js
│ │ │ ├── modules
│ │ │ │ └── notification
│ │ │ │ │ ├── actionTypes.js
│ │ │ │ │ ├── actions.js
│ │ │ │ │ ├── components
│ │ │ │ │ ├── Notifications.js
│ │ │ │ │ └── useNotifications.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── reducer.js
│ │ │ ├── routes
│ │ │ │ ├── Account
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── AccountForm
│ │ │ │ │ │ │ ├── AccountForm.enhancer.js
│ │ │ │ │ │ │ ├── AccountForm.js
│ │ │ │ │ │ │ ├── AccountForm.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ ├── AccountPage
│ │ │ │ │ │ │ ├── AccountPage.enhancer.js
│ │ │ │ │ │ │ ├── AccountPage.js
│ │ │ │ │ │ │ ├── AccountPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── ProviderDataForm
│ │ │ │ │ │ │ ├── ProviderDataForm.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── Home
│ │ │ │ │ ├── components
│ │ │ │ │ │ └── HomePage
│ │ │ │ │ │ │ ├── HomePage.js
│ │ │ │ │ │ │ ├── HomePage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── Login
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── LoginForm
│ │ │ │ │ │ │ ├── LoginForm.enhancer.js
│ │ │ │ │ │ │ ├── LoginForm.js
│ │ │ │ │ │ │ ├── LoginForm.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── LoginPage
│ │ │ │ │ │ │ ├── LoginPage.enhancer.js
│ │ │ │ │ │ │ ├── LoginPage.js
│ │ │ │ │ │ │ ├── LoginPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── NotFound
│ │ │ │ │ ├── components
│ │ │ │ │ │ └── NotFoundPage
│ │ │ │ │ │ │ ├── NotFoundPage.js
│ │ │ │ │ │ │ ├── NotFoundPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── Projects
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── NewProjectDialog
│ │ │ │ │ │ │ ├── NewProjectDialog.enhancer.js
│ │ │ │ │ │ │ ├── NewProjectDialog.js
│ │ │ │ │ │ │ ├── NewProjectDialog.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ ├── NewProjectTile
│ │ │ │ │ │ │ ├── NewProjectTile.js
│ │ │ │ │ │ │ ├── NewProjectTile.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ ├── ProjectTile
│ │ │ │ │ │ │ ├── ProjectTile.js
│ │ │ │ │ │ │ ├── ProjectTile.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── ProjectsPage
│ │ │ │ │ │ │ ├── ProjectsPage.enhancer.js
│ │ │ │ │ │ │ ├── ProjectsPage.js
│ │ │ │ │ │ │ ├── ProjectsPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── routes
│ │ │ │ │ │ └── Project
│ │ │ │ │ │ ├── components
│ │ │ │ │ │ └── ProjectPage
│ │ │ │ │ │ │ ├── ProjectPage.enhancer.js
│ │ │ │ │ │ │ ├── ProjectPage.js
│ │ │ │ │ │ │ ├── ProjectPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── index.js
│ │ │ │ ├── Signup
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── SignupForm
│ │ │ │ │ │ │ ├── SignupForm.enhancer.js
│ │ │ │ │ │ │ ├── SignupForm.js
│ │ │ │ │ │ │ ├── SignupForm.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── SignupPage
│ │ │ │ │ │ │ ├── SignupPage.enhancer.js
│ │ │ │ │ │ │ ├── SignupPage.js
│ │ │ │ │ │ │ ├── SignupPage.styles.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ ├── static
│ │ │ │ ├── User.png
│ │ │ │ └── logo.svg
│ │ │ ├── store
│ │ │ │ ├── createStore.js
│ │ │ │ ├── location.js
│ │ │ │ └── reducers.js
│ │ │ ├── theme.js
│ │ │ └── utils
│ │ │ │ ├── components.js
│ │ │ │ ├── form.js
│ │ │ │ ├── hooks.js
│ │ │ │ ├── index.js
│ │ │ │ └── router.js
│ │ ├── storage.rules
│ │ └── yarn.lock
│ ├── react-native-firebase
│ │ ├── .babelrc
│ │ ├── .gitignore
│ │ ├── .watchmanconfig
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── README.md
│ │ ├── app.json
│ │ ├── index.android.js
│ │ ├── index.ios.js
│ │ ├── ios
│ │ │ ├── GoogleService-Info.plist
│ │ │ ├── Podfile
│ │ │ ├── Podfile.lock
│ │ │ ├── reactnativefirebase.xcodeproj
│ │ │ │ ├── project.pbxproj
│ │ │ │ └── xcshareddata
│ │ │ │ │ └── xcschemes
│ │ │ │ │ ├── reactnativefirebase-tvOS.xcscheme
│ │ │ │ │ └── reactnativefirebase.xcscheme
│ │ │ ├── reactnativefirebase.xcworkspace
│ │ │ │ └── contents.xcworkspacedata
│ │ │ ├── reactnativefirebase
│ │ │ │ ├── AppDelegate.h
│ │ │ │ ├── AppDelegate.m
│ │ │ │ ├── Base.lproj
│ │ │ │ │ └── LaunchScreen.xib
│ │ │ │ ├── Images.xcassets
│ │ │ │ │ └── AppIcon.appiconset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Info.plist
│ │ │ │ └── main.m
│ │ │ └── reactnativefirebaseTests
│ │ │ │ ├── Info.plist
│ │ │ │ └── reactnativefirebaseTests.m
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Home.js
│ │ │ ├── NewTodo.js
│ │ │ ├── TodosList.js
│ │ │ ├── createStore.js
│ │ │ ├── index.js
│ │ │ ├── reducers.js
│ │ │ └── utils.js
│ │ └── yarn.lock
│ ├── react-native
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .watchmanconfig
│ │ ├── App.js
│ │ ├── README.md
│ │ ├── __tests__
│ │ │ └── App-test.js
│ │ ├── app.json
│ │ ├── assets
│ │ │ ├── fonts
│ │ │ │ └── SpaceMono-Regular.ttf
│ │ │ └── images
│ │ │ │ ├── icon.png
│ │ │ │ ├── robot-dev.png
│ │ │ │ ├── robot-prod.png
│ │ │ │ └── splash.png
│ │ ├── babel.config.js
│ │ ├── components
│ │ │ ├── StyledText.js
│ │ │ ├── TabBarIcon.js
│ │ │ ├── Todo.js
│ │ │ ├── TodosList.js
│ │ │ └── __tests__
│ │ │ │ └── StyledText-test.js
│ │ ├── config.js
│ │ ├── constants
│ │ │ ├── Colors.js
│ │ │ └── Layout.js
│ │ ├── navigation
│ │ │ ├── AppNavigator.js
│ │ │ └── MainTabNavigator.js
│ │ ├── package.json
│ │ ├── reducer.js
│ │ ├── screens
│ │ │ ├── HomeScreen.js
│ │ │ ├── LinksScreen.js
│ │ │ └── SettingsScreen.js
│ │ ├── store.js
│ │ └── yarn.lock
│ ├── simple
│ │ ├── .env
│ │ ├── .firebaserc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ └── manifest.json
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── App.js
│ │ │ ├── App.test.js
│ │ │ ├── Home.js
│ │ │ ├── NewTodo.js
│ │ │ ├── Todo.css
│ │ │ ├── TodoItem.js
│ │ │ ├── Todos.js
│ │ │ ├── config.js
│ │ │ ├── favicon.ico
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── initFirebase.js
│ │ │ ├── logo.svg
│ │ │ ├── reducer.js
│ │ │ └── store.js
│ │ └── yarn.lock
│ └── typescript
│ │ ├── .env
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ │ ├── src
│ │ ├── AddTodo.tsx
│ │ ├── App.css
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── Home.tsx
│ │ ├── Todo.tsx
│ │ ├── Todos.tsx
│ │ ├── config.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── reducer.ts
│ │ ├── serviceWorker.ts
│ │ └── store.ts
│ │ ├── tsconfig.json
│ │ ├── tslint.json
│ │ └── yarn.lock
└── snippets
│ ├── decorators
│ ├── App.js
│ ├── README.md
│ └── TodoItem.js
│ ├── multipleQueries
│ ├── App.js
│ ├── README.md
│ └── TodoItem.js
│ ├── populates
│ ├── App.js
│ └── README.md
│ ├── stateBasedQuery
│ ├── Home.js
│ ├── README.md
│ └── Todos.js
│ ├── watchEvent
│ ├── Basic.js
│ ├── README.md
│ └── Recompose.js
│ └── webpack2
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── src
│ ├── app.js
│ ├── config.js
│ ├── reducer.js
│ └── store.js
│ └── webpack.config.js
├── index.d.ts
├── package-lock.json
├── package.json
├── src
├── ReactReduxFirebaseContext.js
├── ReactReduxFirebaseProvider.js
├── ReduxFirestoreContext.js
├── ReduxFirestoreProvider.js
├── actions
│ ├── auth.js
│ ├── query.js
│ └── storage.js
├── constants.js
├── createFirebaseInstance.js
├── firebaseConnect.js
├── firestoreConnect.js
├── helpers.js
├── index.js
├── reducer.js
├── reducers.js
├── useFirebase.js
├── useFirebaseConnect.js
├── useFirestore.js
├── useFirestoreConnect.js
├── utils
│ ├── actions.js
│ ├── auth.js
│ ├── events.js
│ ├── index.js
│ ├── populate.js
│ ├── query.js
│ ├── reducers.js
│ └── storage.js
├── withFirebase.js
└── withFirestore.js
├── test
├── .eslintrc.js
├── mocha.opts
├── mockData.js
├── setup.js
├── unit
│ ├── actions
│ │ ├── auth.spec.js
│ │ ├── query.spec.js
│ │ └── storage.spec.js
│ ├── createFirebaseInstance.spec.js
│ ├── firebaseConnect.spec.js
│ ├── firestoreConnect.spec.js
│ ├── helpers.spec.js
│ ├── library.spec.js
│ ├── reducer.spec.js
│ ├── useFirebase.spec.js
│ ├── useFirebaseConnect.spec.js
│ ├── useFirestore.spec.js
│ ├── useFirestoreConnect.spec.js
│ ├── utils
│ │ ├── actions.spec.js
│ │ ├── auth.spec.js
│ │ ├── events.spec.js
│ │ ├── index.spec.js
│ │ ├── populate.spec.js
│ │ ├── query.spec.js
│ │ └── storage.spec.js
│ ├── withFirebase.spec.js
│ └── withFirestore.spec.js
└── utils.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["minify", {
4 | "mangle": false
5 | }],
6 | "@babel/preset-react",
7 | ["@babel/env", {
8 | "targets": {
9 | "chrome": 52,
10 | "browsers": ["last 2 versions", "safari >= 7"]
11 | }
12 | }]
13 | ],
14 | "plugins": [
15 | "lodash",
16 | "add-module-exports",
17 | "@babel/plugin-proposal-class-properties"
18 | ],
19 | "env": {
20 | "es": {
21 | "comments": false
22 | },
23 | "commonjs": {
24 | "comments": false
25 | },
26 | "test": {
27 | "plugins": [
28 | "@babel/plugin-transform-runtime",
29 | "@babel/transform-async-to-generator",
30 | ["module-resolver", {
31 | "root": ["./src"]
32 | }]
33 | ]
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2" # required to adjust maintainability checks
2 |
3 | languages:
4 | JavaScript: true
5 |
6 | checks:
7 | file-lines:
8 | enabled: true
9 | config:
10 | threshold: 740
11 | return-statements:
12 | enabled: true
13 | config:
14 | threshold: 6
15 |
16 | exclude_patterns:
17 | - "lib/**"
18 | - "test/**"
19 | - "examples/**"
20 | - "LICENSE"
21 | - "LICENSE.md"
22 | - "README.md"
23 | - "package.json"
24 | - "**/*.d.ts"
25 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage/**
2 | node_modules/**
3 | dist/**
4 | es/**
5 | lib/**
6 | _book/**
7 | _site/**
8 | docs/**
9 | index.d.ts
10 | examples/**
11 | test/utils.js
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | extends: ['standard', 'standard-react', 'prettier', 'prettier/react'],
5 | plugins: ['babel', 'react', 'prettier', 'react-hooks'],
6 | settings: {
7 | react: {
8 | version: 'detect'
9 | }
10 | },
11 | env: {
12 | browser: true,
13 | es6: true
14 | },
15 | rules: {
16 | semi: [2, 'never'],
17 | 'no-console': 'error',
18 | 'prettier/prettier': [
19 | 'error',
20 | {
21 | singleQuote: true,
22 | trailingComma: 'none',
23 | semi: false,
24 | bracketSpacing: true,
25 | jsxBracketSameLine: true,
26 | printWidth: 80,
27 | tabWidth: 2,
28 | useTabs: false
29 | }
30 | ]
31 | },
32 | overrides: [
33 | {
34 | files: ['./src/**/**.js'],
35 | plugins: ['jsdoc'],
36 | extends: ['plugin:jsdoc/recommended'],
37 | rules: {
38 | 'jsdoc/newline-after-description': 0,
39 | 'jsdoc/no-undefined-types': [1, { definedTypes: ['React', 'firebase'] }]
40 | }
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: prescottprue
5 | open_collective: react-redux-firebase
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Do you want to request a *feature* or report a *bug*?**
2 |
3 | (If this is a *usage question*, please **do not post it here**—post it on [gitter](https://gitter.im/redux-firebase/Lobby). If this is not a “feature” or a “bug”, or the phrase “How do I...?” applies, then it's probably a usage question.)
4 |
5 |
6 | **What is the current behavior?**
7 |
8 |
9 |
10 | **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via [codesandbox](https://codesandbox.io/) or similar.**
11 |
12 |
13 |
14 | **What is the expected behavior?**
15 |
16 |
17 |
18 | **Which versions of dependencies, and which browser and OS are affected by this issue? Did this work in previous versions or setups?**
19 |
20 |
22 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Description
2 |
3 |
4 | ### Check List
5 | If not relevant to pull request, check off as complete
6 |
7 | - [ ] All tests passing
8 | - [ ] Docs updated with any changes or examples if applicable
9 | - [ ] Added tests to ensure new feature(s) work properly
10 |
11 | ### Relevant Issues
12 |
13 |
--------------------------------------------------------------------------------
/.github/workflows/verify.yml:
--------------------------------------------------------------------------------
1 | name: Verify
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | build:
7 | name: Build
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | node-version: [10.x, 12.x, 14.x]
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 |
21 | # Setup dependency caching
22 | - uses: actions/cache@v1
23 | with:
24 | path: ~/.npm
25 | key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
26 |
27 | - name: Install Dependencies
28 | run: npm ci
29 |
30 | - name: Check For Lint
31 | run: npm run lint
32 |
33 | - name: Run Unit Tests + Coverage
34 | run: npm run test:cov
35 |
36 | - name: Run Build
37 | run: npm run build
38 |
39 | - name: Upload Coverage
40 | if: matrix.node-version == '10.x'
41 | env:
42 | CODE_COV: ${{ secrets.CODE_COV }}
43 | # Upload to codecov.io. Curl used in place of codecov/codecov-action
44 | # due to long build time. See https://github.com/codecov/codecov-action/issues/21
45 | run: curl -s https://codecov.io/bash | bash -s -- -t $CODE_COV
46 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 | bin
3 | examples
4 | tests
5 | coverage
6 | .istanbul.yml
7 | _book
8 | .babelrc
9 | SUMMARY.md
10 | webpack.config.js
11 | .eslintignore
12 | .eslintrc
13 | book.json
14 | CNAME
15 | .github
16 | yarn.lock
17 | .travis.yml
18 | CODE_OF_CONDUCT.md
19 | CHANGELOG.md
20 | .codeclimate.yml
21 | PATRONS.md
22 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | semi: false
3 | trailingComma: 'none'
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 | Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/prescottprue/react-redux-firebase/releases) page.
5 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | react-redux-firebase.com
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-present Prescott Prue
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PATRONS.md:
--------------------------------------------------------------------------------
1 | # Patrons
2 |
3 | Meet some of the outstanding companies and individuals that made it possible:
4 |
5 | * [Reside Network Inc.](https://github.com/reside-eng)
6 |
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "gitbook": ">=3.2.1",
3 | "title": "React Redux Firebase",
4 | "plugins": ["edit-link", "prism", "-highlight", "github", "anchorjs", "versions-select", "ga"],
5 | "pluginsConfig": {
6 | "edit-link": {
7 | "base": "https://github.com/prescottprue/react-redux-firebase/tree/master",
8 | "label": "Edit This Page"
9 | },
10 | "github": {
11 | "url": "https://github.com/prescottprue/react-redux-firebase/"
12 | },
13 | "theme-default": {
14 | "styles": {
15 | "website": "build/gitbook.css"
16 | }
17 | },
18 | "ga": {
19 | "token": "UA-102355407-1"
20 | },
21 | "versions": {
22 | "gitbookConfigURL": "https://storage.googleapis.com/docs.react-redux-firebase.com/book.json",
23 | "options": [
24 | {
25 | "value": "http://react-redux-firebase.com/",
26 | "text": "Version 3.0.0",
27 | "selected": true
28 | },
29 | {
30 | "value": "http://docs.react-redux-firebase.com/history/v2.0.0/",
31 | "text": "Version 2.0.0"
32 | },
33 | {
34 | "value": "http://docs.react-redux-firebase.com/history/v1.5.0/",
35 | "text": "Version 1.5.0"
36 | },
37 | {
38 | "value": "http://docs.react-redux-firebase.com/history/v1.4.0/",
39 | "text": "Version 1.4.0"
40 | }
41 | ]
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | notify:
3 | require_ci_to_pass: true
4 | comment:
5 | behavior: default
6 | layout: header, diff
7 | require_changes: false
8 | coverage:
9 | precision: 2
10 | range:
11 | - 70.0
12 | - 100.0
13 | round: down
14 | status:
15 | changes: false
16 | patch: true
17 | project: true
18 | parsers:
19 | gcov:
20 | branch_detection:
21 | conditional: true
22 | loop: true
23 | macro: false
24 | method: false
25 | javascript:
26 | enable_partials: false
27 |
--------------------------------------------------------------------------------
/docs/GLOSSARY.md:
--------------------------------------------------------------------------------
1 | # Glossary
2 |
3 | ## ## `profileDecorator`
4 |
5 |
--------------------------------------------------------------------------------
/docs/api/README.md:
--------------------------------------------------------------------------------
1 | # API Reference
2 |
3 | Just like [redux](http://redux.js.org/docs/api/index.html), the react-redux-firebase API surface is intentionally as small as possible.
4 |
5 | ## Top-Level Exports
6 | * [useFirebase](/docs/api/useFirebase.md#usefirebase)
7 | * [useFirebaseConnect](/docs/api/useFirebaseConnect.md#usefirebaseconnect)
8 | * [useFirestore](/docs/api/useFirestore.md#usefirestore)
9 | * [useFirestoreConnect](/docs/api/useFirestoreConnect.md#usefirebaseconnect)
10 | * [firebaseConnect](/docs/api/firebaseConnect.md#firebaseconnect)
11 | * [withFirebase](/docs/api/withFirebase.md)
12 | * [firestoreConnect](/docs/api/firestoreConnect.md)
13 | * [withFirestore](/docs/api/withFirestore.md)
14 | * [reducer](/docs/api/reducer.md) (also exported as `firebaseReducer`)
15 | * [constants](/docs/api/constants.md)
16 | * [actionTypes](/docs/api/constants.md)
17 | * [isLoaded](/docs/api/helpers.md#isLoaded)
18 | * [isEmpty](/docs/api/helpers.md#isEmpty)
19 | * [populate](/docs/api/helpers.md#populate)
20 |
21 | ## Importing
22 |
23 | Every function described above is a top-level export. You can import any of them like this:
24 |
25 | ### ES6
26 | ```js
27 | import { firebaseConnect } from 'react-redux-firebase'
28 | ```
29 |
30 | ### ES5 (CommonJS)
31 | ```js
32 | var firebaseConnect = require('react-redux-firebase').firebaseConnect
33 | ```
34 |
--------------------------------------------------------------------------------
/docs/api/ReactReduxFirebaseContext.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [ReactReduxFirebaseContext][1]
6 |
7 | ## ReactReduxFirebaseContext
8 |
9 | Context for extended firebase instance created
10 | by react-redux-firebase
11 |
12 | [1]: #reactreduxfirebasecontext
13 |
--------------------------------------------------------------------------------
/docs/api/ReactReduxFirebaseProvider.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [ReactReduxFirebaseProvider][1]
6 | - [Parameters][2]
7 |
8 | ## ReactReduxFirebaseProvider
9 |
10 |
11 | Provider for context containing extended firebase
12 | instance created by react-redux-firebase.
13 |
14 | ### Parameters
15 |
16 | - `props` **[object][4]** Component props (optional, default `{}`)
17 | - `props.config` **[object][4]** react-redux-firebase config
18 | - `props.dispatch` **[Function][5]** Redux's dispatch function
19 | - `props.firebase` **[object][4]** Firebase library
20 | - `props.initializeAuth` **[boolean][6]** Whether or not to initialize auth
21 | - `props.createFirestoreInstance` **[Function][5]** Function for creating
22 | extended firestore instance
23 |
24 | Returns **React.Context.Provider** Provider for react-redux-firebase context
25 |
26 | [1]: #reactreduxfirebaseprovider
27 |
28 | [2]: #parameters
29 |
30 | [3]: https://react-redux-firebase.com/api/docs/ReactReduxFirebaseProvider.html
31 |
32 | [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
33 |
34 | [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
35 |
36 | [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
37 |
--------------------------------------------------------------------------------
/docs/api/ReduxFirestoreContext.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [ReduxFirestoreContext][1]
6 |
7 | ## ReduxFirestoreContext
8 |
9 | Context for extended firebase instance created
10 | by react-redux-firebase
11 |
12 | [1]: #reduxfirestorecontext
13 |
--------------------------------------------------------------------------------
/docs/api/ReduxFirestoreProvider.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [ReduxFirestoreProvider][1]
6 | - [Parameters][2]
7 |
8 | ## ReduxFirestoreProvider
9 |
10 |
11 | Provider for context containing extended firestore instance created
12 | by react-redux-firebase
13 |
14 | ### Parameters
15 |
16 | - `props` **[object][4]** Component props (optional, default `{}`)
17 | - `props.config` **[object][4]** react-redux-firebase config
18 | - `props.dispatch` **[Function][5]** Redux's dispatch function
19 | - `props.firebase` **[object][4]** Firebase library
20 | - `props.initializeAuth` **[boolean][6]** Whether or not to initialize auth
21 | - `props.createFirestoreInstance` **[Function][5]** Function for creating
22 | extended firestore instance
23 |
24 | Returns **React.Context.Provider** Provider for redux-firestore context
25 |
26 | [1]: #reduxfirestoreprovider
27 |
28 | [2]: #parameters
29 |
30 | [3]: https://react-redux-firebase.com/docs/api/ReduxFirestoreProvider.html
31 |
32 | [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
33 |
34 | [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
35 |
36 | [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
37 |
--------------------------------------------------------------------------------
/docs/api/getFirebase.md:
--------------------------------------------------------------------------------
1 | # getFirebase
2 |
3 | Expose [extended Firebase instance](/docs/api/firebaseInstance.md) created internally. Useful for
4 | integrations into external libraries such as redux-thunk and redux-observable.
5 |
6 | The methods which are available are documented in [firebaseInstance](/docs/api/firebaseInstance.md)
7 |
8 | **Examples**
9 |
10 | _redux-thunk integration_
11 |
12 | ```javascript
13 | import { applyMiddleware, compose, createStore } from 'redux';
14 | import thunk from 'redux-thunk';
15 | import { getFirebase } from 'react-redux-firebase';
16 | import makeRootReducer from './reducers';
17 |
18 | const store = createStore(
19 | makeRootReducer(),
20 | initialState,
21 | compose(
22 | applyMiddleware([
23 | // Pass getFirebase function as extra argument
24 | thunk.withExtraArgument(getFirebase)
25 | ])
26 | )
27 | );
28 |
29 | // then later
30 | export function addTodo(newTodo) {
31 | return (dispatch, getState, getFirebase) => {
32 | const firebase = getFirebase()
33 | firebase
34 | .push('todos', newTodo)
35 | .then(() => {
36 | dispatch({ type: 'SOME_ACTION' })
37 | })
38 | }
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/api/reducer.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [reducer][1]
6 | - [Parameters][2]
7 |
8 | ## reducer
9 |
10 | ### Parameters
11 |
12 | - `state` **[object][3]** Current Firebase Redux State (state.firebase)
13 | - `action` **[object][3]** Action which will modify state
14 | - `action.type` **[string][4]** Type of Action being called
15 | - `action.path` **[string][4]** Path of action that was dispatched
16 | - `action.data` **[string][4]** Data associated with action
17 |
18 | Returns **[object][3]** Firebase redux state
19 |
20 | [1]: #reducer
21 |
22 | [2]: #parameters
23 |
24 | [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
25 |
26 | [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
27 |
--------------------------------------------------------------------------------
/docs/api/useFirebase.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [useFirebase][1]
6 | - [Examples][2]
7 |
8 | ## useFirebase
9 |
10 |
11 | React hook that provides `firebase` object.
12 | Firebase is gathered from ReactReduxFirebaseContext, which is
13 | set by createFirebaseInstance during setup.
14 | **NOTE**: This version of the Firebase library has extra methods, config,
15 | and functionality which give it it's capabilities such as dispatching
16 | actions.
17 |
18 | ### Examples
19 |
20 | _Basic_
21 |
22 | ```javascript
23 | import { useFirebase } from 'react-redux-firebase'
24 |
25 | export default function AddData() {
26 | const firebase = useFirebase()
27 |
28 | function addTodo() {
29 | const exampleTodo = { done: false, text: 'Sample' }
30 | return firebase.push('todos', exampleTodo)
31 | }
32 |
33 | return (
34 |
35 |
36 | Add Sample Todo
37 |
38 |
39 | )
40 | }
41 | ```
42 |
43 | Returns **[object][4]** Extended Firebase instance
44 |
45 | [1]: #usefirebase
46 |
47 | [2]: #examples
48 |
49 | [3]: https://react-redux-firebase.com/docs/api/useFirebase.html
50 |
51 | [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
52 |
--------------------------------------------------------------------------------
/docs/api/useFirestore.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Table of Contents
4 |
5 | - [useFirestore][1]
6 | - [Examples][2]
7 |
8 | ## useFirestore
9 |
10 |
11 | React hook that return firestore object.
12 |
13 | ### Examples
14 |
15 | _Basic_
16 |
17 | ```javascript
18 | import React from 'react'
19 | import { useFirestore } from 'react-redux-firebase'
20 |
21 | export default function AddData() {
22 | const firestore = useFirestore()
23 |
24 | function addTodo() {
25 | const exampleTodo = { done: false, text: 'Sample' }
26 | return firestore.collection('todos').add(exampleTodo)
27 | }
28 |
29 | return (
30 |
31 |
32 | Add Sample Todo
33 |
34 |
35 | )
36 | }
37 | ```
38 |
39 | Returns **[object][4]** Extended Firestore instance
40 |
41 | [1]: #usefirestore
42 |
43 | [2]: #examples
44 |
45 | [3]: https://react-redux-firebase.com/docs/api/useFirestore.html
46 |
47 | [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
48 |
--------------------------------------------------------------------------------
/docs/integrations/README.md:
--------------------------------------------------------------------------------
1 | # Integrations
2 |
3 | This section includes some integrations for using `react-redux-firebase` within other libraries.
4 |
5 | * [react-chrome-redux](/docs/integrations/react-chrome-redux.md)
6 | * [react-native](/docs/integrations/react-native.md)
7 | * [redux-form](/docs/integrations/redux-form.md)
8 | * [redux-observable](/docs/integrations/redux-observable.md)
9 | * [redux-persist](/docs/integrations/redux-persist.md)
10 | * [redux-saga](/docs/integrations/redux-saga.md)
11 |
--------------------------------------------------------------------------------
/docs/integrations/react-chrome-redux.md:
--------------------------------------------------------------------------------
1 | # React Chrome Redux
2 |
3 | > Recipe for integrating with [`react-chrome-redux`](https://github.com/tshaddix/react-chrome-redux)
4 |
5 | **NOTE:** This recipe is based communications within [issue #157](https://github.com/prescottprue/react-redux-firebase/issues/157) and is not considered "completed". If you have suggestions, please post an issue or reach out over [gitter](https://gitter.im/redux-firebase/Lobby).
6 |
7 | Do not use `firebaseConnect` in your content/popup scripts.
8 |
9 | Use `react-redux-firebase` in the background script, and communicate with your content/popup using the proxy store and aliases.
10 |
11 | Following this pattern allows authenticating the user from the popup:
12 |
13 | ```js
14 | // in popup, display a login form (component named LoginForm)
15 | // ...
16 | const store = new Store({
17 | portName: 'example'
18 | });
19 | // ...
20 | onSubmit(e) {
21 | e.preventDefault();
22 | if (this.isValid()) {
23 | // USER_LOGGING_IN is defined as an alias in react-chrome-redux
24 | store.dispatch({ type: 'USER_LOGGING_IN', data: {email: "test", password: "test"}});
25 | }
26 | }
27 | // ...
28 | // Do not call firebaseConnect here
29 | export default connect(null, { login })(LoginForm);
30 | ```
31 |
32 | Then, create your alias in the background script, import `react-redux-firebase` as well as `redux-thunk` to wait for Firebase's reply before updating the state (see reply in [issue #84 on react-chrome-redux](https://github.com/tshaddix/react-chrome-redux/issues/84)).
33 |
34 | ```js
35 | // in event (background script)
36 | // ...
37 | const store = createStore(
38 | rootReducer,
39 | {}
40 | )
41 |
42 | wrapStore(store, {
43 | portName: 'example'
44 | });
45 | ```
46 |
--------------------------------------------------------------------------------
/docs/integrations/redux-saga.md:
--------------------------------------------------------------------------------
1 | # Redux Saga Recipes
2 |
3 | ### Example
4 |
5 | ```javascript
6 | import { applyMiddleware, compose, createStore } from 'redux'
7 | import { browserHistory } from 'react-router'
8 | import makeRootReducer from './reducers'
9 | import createSagaMiddleware from 'redux-saga'
10 | import firebase from 'firebase/app';
11 | import 'firebase/database';
12 |
13 | const firebaseConfig = {} // firebase configuration including databaseURL
14 | const reduxFirebase = {
15 | userProfile: 'users',
16 | enableLogging: 'false'
17 | }
18 |
19 | firebase.initializeApp(firebaseConfig);
20 |
21 | function* helloSaga() {
22 | try {
23 | yield firebase.ref('/some/path').push({ nice: 'work!' })
24 | } catch(err) {
25 | console.log('Error in saga!:', err)
26 | }
27 | }
28 |
29 | export default (initialState = {}, history) => {
30 |
31 | const sagaMiddleware = createSagaMiddleware() // create middleware
32 |
33 | const middleware = [ sagaMiddleware ]
34 |
35 | const store = createStore(
36 | makeRootReducer(),
37 | {}, // initial state
38 | compose(
39 | applyMiddleware(...middleware)
40 | )
41 | )
42 |
43 | return store
44 | }
45 |
46 | // when calling saga
47 | sagaMiddleware.run(helloSaga)
48 | ```
49 |
--------------------------------------------------------------------------------
/docs/integrations/reselect.md:
--------------------------------------------------------------------------------
1 | # Reselect
2 |
3 | There are a number of reasons to use state selectors, as mentioned in the [relesect docs](https://github.com/reduxjs/reselect):
4 |
5 | > * Selectors can compute derived data, allowing Redux to store the minimal possible state.
6 | > * Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
7 | > * Selectors are composable. They can be used as input to other selectors.
8 |
9 | For more information, about why this is important, checkout the [motivation for memoized selectors sections of the reselect docs](https://github.com/reduxjs/reselect#motivation-for-memoized-selectors)
10 |
11 | ## State Selectors
12 |
13 | Select only what you need from state in your selectors instead of the whole firebase/firestore state object:
14 |
15 | ```js
16 | import { createSelector } from 'reselect';
17 | import { connect } from 'react-redux'
18 | import { get, sumBy } from 'lodash'
19 |
20 | const netTotalSelector = createSelector(
21 | state => get(state, 'firestore.data.products'),
22 | products => sumBy(products, 'price')
23 | )
24 |
25 | connect((state) => ({
26 | netTotal: netTotalSelector(state)
27 | }))(Component)
28 | ```
29 |
30 | In this case Reselect will memoize the products object. That means that even if there's any update to other parts of redux state (including firebase/firestore), the memoized products object will stay the same until there is an update to the products themselves.
31 |
32 | See [issue #614](https://github.com/prescottprue/react-redux-firebase/issues/614) for more info.
--------------------------------------------------------------------------------
/docs/recipes/populate.md:
--------------------------------------------------------------------------------
1 | # Populate
2 |
3 | ### Populate Key Within Items in a List
4 |
5 | Populate the owner of each item in a todos list from the 'users' root.
6 |
7 | #### Examples
8 |
9 | ##### Populate List of Items
10 |
11 | ```javascript
12 | import { compose } from 'redux'
13 | import { connect } from 'react-redux'
14 | import { firebaseConnect, populate } from 'react-redux-firebase'
15 |
16 | const populates = [
17 | { child: 'owner', root: 'users' }
18 | ]
19 |
20 | const enhance = compose(
21 | firebaseConnect([
22 | { path: 'todos', populates }
23 | ]),
24 | connect(
25 | ({ firebase }) => ({
26 | todos: populate(firebase, 'todos', populates),
27 | })
28 | )
29 | )
30 |
31 | export default enhance(SomeComponent)
32 | ```
33 |
34 | ### Populate Profile Parameters
35 |
36 | To Populate parameters within profile/user object, include the `profileParamsToPopulate` parameter within your configuration as well as using `populate`.
37 |
38 | **NOTE** Using `profileParamsToPopulate` no longer automatically populates profile, you must use `populate`. Un-populated profile lives within state under `state.firebase.profile`.
39 |
40 | #### Examples
41 |
42 | ##### Populate Role
43 |
44 | Populating a user's role parameter from a list of roles (under `roles` collection).
45 |
46 | ```javascript
47 | export const profilePopulates = [{ child: 'role', root: 'roles' }]
48 | const config = {
49 | userProfile: 'users',
50 | profileParamsToPopulate: profilePopulates // populate list of todos from todos ref
51 | }
52 |
53 | // Wrapping some component
54 | connect(
55 | ({ firebase }) => ({
56 | profile: firebase.profile,
57 | populatedProfile: populate(firebase, 'profile', profilePopulates),
58 | })
59 | )(SomeComponent)
60 | ```
61 |
--------------------------------------------------------------------------------
/docs/recipes/ssr.md:
--------------------------------------------------------------------------------
1 | # Server Side Rendering
2 |
3 | ## Preload Data
4 | Preloading data is a common step to in serverside rendering. How it is done differs based on whether you are using Real Time Database or Firestore.
5 |
6 | **Real Time Database**
7 | `promiseEvents`, which is similar to `firebaseConnect` expected it is presented as a function instead of a React Component.
8 |
9 | After creating your store:
10 |
11 | ```js
12 | store.firebase
13 | .promiseEvents([
14 | { path: 'todos' },
15 | { path: 'users' }
16 | ])
17 | .then(() => {
18 | console.log('data is loaded into redux store')
19 | })
20 | ```
21 |
22 | **Firestore**
23 | Its just as simple as calling the built in get method with your query config:
24 |
25 | ```js
26 | store.firestore.get({ collection: 'todos' }) // or .get('todos')
27 | .then(() => {
28 | console.log('data is loaded into redux store')
29 | })
30 | ```
31 |
32 | ## Troubleshooting
33 |
34 | ### Include XMLHttpRequest
35 |
36 | ```js
37 | // needed to fix "Error: The XMLHttpRequest compatibility library was not found."
38 | global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
39 | ```
40 |
41 | If you find adding this extra code to be an annoyance or you would like to discuss a different way to do it, please feel free to open an issue/pull request or reach out on [gitter](https://gitter.im/redux-firebase/Lobby).
42 |
43 |
44 |
45 | ## Implement with Webpack
46 |
47 | ### Make Sure Loaders are setup appropriately
48 | Note, make sure that you have excluded `node_modules` in your webpack loaders
49 |
50 | ```js
51 | // webpack 1
52 | exclude: /node_modules/,
53 | // or webpack 2/3
54 | options: { presets: [ [ 'es2015', { modules: false } ] ] }
55 | ```
56 |
--------------------------------------------------------------------------------
/docs/roadmap.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | Please visit [the v3.0.0 Roadmap wiki doc](https://github.com/prescottprue/react-redux-firebase/wiki/v3.0.0-Roadmap)
4 |
--------------------------------------------------------------------------------
/docs/static/BuildPhase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/BuildPhase.png
--------------------------------------------------------------------------------
/docs/static/FirebaseOverview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/FirebaseOverview.png
--------------------------------------------------------------------------------
/docs/static/PlistDownload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/PlistDownload.png
--------------------------------------------------------------------------------
/docs/static/RegisterApp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/RegisterApp.png
--------------------------------------------------------------------------------
/docs/static/UrlTypes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/UrlTypes.png
--------------------------------------------------------------------------------
/docs/static/dataFlow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/docs/static/dataFlow.png
--------------------------------------------------------------------------------
/examples/complete/firestore/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/examples/complete/firestore/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': ['standard', 'standard-react', 'prettier', 'prettier/react'],
3 | root: true,
4 | parser: 'babel-eslint',
5 | plugins: ['import', 'react', 'react-hooks', 'prettier'],
6 | settings: {
7 | react: {
8 | version: '16.12'
9 | },
10 | 'import/resolver': {
11 | node: {
12 | moduleDirectory: ['node_modules', '/']
13 | }
14 | }
15 | },
16 | rules: {
17 | semi: [
18 | 2, 'never'
19 | ],
20 | 'no-console': 'error',
21 | 'react/forbid-prop-types': 0,
22 | 'react/require-default-props': 0,
23 | 'react/jsx-filename-extension': 0,
24 | 'import/no-named-as-default': 0,
25 | 'no-return-await': 2,
26 | 'react-hooks/rules-of-hooks': 'error',
27 | 'react-hooks/exhaustive-deps': 'warn',
28 | 'prettier/prettier': [
29 | 'error',
30 | {
31 | singleQuote: true,
32 | trailingComma: 'none',
33 | semi: false,
34 | bracketSpacing: true,
35 | jsxBracketSameLine: true,
36 | printWidth: 80,
37 | tabWidth: 2,
38 | useTabs: false
39 | }
40 | ]
41 | },
42 | overrides: [
43 | {
44 | files: ['./src/**/*.test.js', './src/**/*.spec.js'],
45 | env: {
46 | jest: true
47 | }
48 | }
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/examples/complete/firestore/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/examples/complete/firestore/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/firestore/public/favicon.ico
--------------------------------------------------------------------------------
/examples/complete/firestore/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/firestore/public/logo192.png
--------------------------------------------------------------------------------
/examples/complete/firestore/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/firestore/public/logo512.png
--------------------------------------------------------------------------------
/examples/complete/firestore/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Firestore Example",
3 | "name": "Firestore + react-redux-firebase example",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/examples/complete/firestore/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 40vh;
19 | max-height: 50vh;
20 | display: flex;
21 | flex-direction: column;
22 | align-items: center;
23 | justify-content: center;
24 | font-size: calc(10px + 2vmin);
25 | color: white;
26 | }
27 |
28 | .App-link {
29 | color: #61dafb;
30 | }
31 |
32 | @keyframes App-logo-spin {
33 | from {
34 | transform: rotate(0deg);
35 | }
36 | to {
37 | transform: rotate(360deg);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import firebase from 'firebase/app'
4 | import 'firebase/auth'
5 | import 'firebase/database'
6 | import 'firebase/firestore' // make sure you add this for firestore
7 | import { ReactReduxFirebaseProvider } from 'react-redux-firebase'
8 | import { createFirestoreInstance } from 'redux-firestore'
9 | import Home from './Home'
10 | import configureStore from './store'
11 | import { firebase as fbConfig, rrfConfig } from './config'
12 | import './App.css'
13 |
14 | const initialState = window && window.__INITIAL_STATE__ // set initial state here
15 | const store = configureStore(initialState)
16 | // Initialize Firebase instance
17 | firebase.initializeApp(fbConfig)
18 |
19 | export default function App() {
20 | return (
21 |
22 |
27 |
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from '@testing-library/react'
3 | import App from './App'
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render( )
7 | const linkElement = getByText(/learn react/i)
8 | expect(linkElement).toBeInTheDocument()
9 | })
10 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Todos from './Todos'
3 | import NewTodo from './NewTodo'
4 | import logo from './logo.svg'
5 | import './App.css'
6 |
7 | function Home() {
8 | return (
9 |
10 |
11 |
firestore demo
12 |
13 |
14 |
15 |
27 |
Todos List
28 |
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default Home
36 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/NewTodo.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useFirestore } from 'react-redux-firebase'
3 |
4 | function NewTodo() {
5 | const [inputVal, changeInput] = useState('')
6 | const firestore = useFirestore()
7 |
8 | function resetInput() {
9 | changeInput('')
10 | }
11 | function onInputChange(e) {
12 | return changeInput(e && e.target && e.target.value)
13 | }
14 |
15 | function addTodo() {
16 | return firestore
17 | .collection('todos')
18 | .add({ text: inputVal || 'sample', done: false })
19 | }
20 |
21 | return (
22 |
23 |
New Todo
24 |
25 | Add
26 | Cancel
27 |
28 | )
29 | }
30 |
31 | export default NewTodo
32 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/Todo.css:
--------------------------------------------------------------------------------
1 | .Todo {
2 | margin: 1rem;
3 |
4 | }
5 | .Todo-Input {
6 | margin: 1rem;
7 | }
8 |
9 | .Todo-Button {
10 | margin: 1rem;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { useSelector } from 'react-redux'
4 | import { useFirestore } from 'react-redux-firebase'
5 | import './Todo.css'
6 |
7 | function TodoItem({ id }) {
8 | const todo = useSelector(
9 | ({ firestore: { data } }) => data.todos && data.todos[id]
10 | )
11 | const firestore = useFirestore()
12 |
13 | function toggleDone() {
14 | firestore.update(`todos/${id}`, { done: !todo.done })
15 | }
16 |
17 | function deleteTodo() {
18 | return firestore.delete(`todos/${id}`)
19 | }
20 |
21 | return (
22 |
23 |
29 | {todo.text || todo.name}
30 |
31 | Delete
32 |
33 |
34 | )
35 | }
36 |
37 | TodoItem.propTypes = {
38 | id: PropTypes.string.isRequired
39 | }
40 |
41 | export default TodoItem
42 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/Todos.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from 'react-redux'
3 | import { useFirestoreConnect, isLoaded, isEmpty } from 'react-redux-firebase'
4 | import TodoItem from './TodoItem'
5 |
6 | const todosQuery = {
7 | collection: 'todos',
8 | limitTo: 10
9 | }
10 |
11 | function Todos() {
12 | // Attach todos listener
13 | useFirestoreConnect(() => [todosQuery])
14 |
15 | // Get todos from redux state
16 | const todos = useSelector(({ firestore: { ordered } }) => ordered.todos)
17 |
18 | // Show a message while todos are loading
19 | if (!isLoaded(todos)) {
20 | return 'Loading'
21 | }
22 |
23 | // Show a message if there are no todos
24 | if (isEmpty(todos)) {
25 | return 'Todo list is empty'
26 | }
27 |
28 | return todos.map(({ id, ...todo }, ind) => (
29 |
30 | ))
31 | }
32 |
33 | export default Todos
34 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/Todos.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render, unmountComponentAtNode } from 'react-dom'
3 | import { act } from 'react-dom/test-utils'
4 | import { useSelector } from 'react-redux'
5 | import Todos from './Todos'
6 |
7 | jest.mock('react-redux')
8 |
9 | describe('Should test the Todos Component', () => {
10 | let container = null
11 |
12 | beforeEach(() => {
13 | // setup a DOM element as a render target
14 | container = document.createElement('div')
15 | document.body.appendChild(container)
16 | })
17 |
18 | afterEach(() => {
19 | // cleanup on exiting
20 | unmountComponentAtNode(container)
21 | container.remove()
22 | container = null
23 | })
24 |
25 | it('show empty text when the todo list is empty', () => {
26 | const todos = []
27 | useSelector.mockReturnValue(todos)
28 |
29 | act(() => {
30 | render( , container)
31 | })
32 |
33 | container.querySelectorAll('li')
34 | expect(container.textContent).toBe('Todo list is empty')
35 | })
36 |
37 | it('should have one TodoItem in the list', () => {
38 | const todos = [
39 | {
40 | id: 'todoId',
41 | name: 'Sample todo',
42 | text: 'This is for testing todos'
43 | }
44 | ]
45 |
46 | useSelector.mockReturnValue(todos)
47 | act(() => {
48 | render( , container)
49 | })
50 |
51 | const listItems = container.querySelectorAll('li')
52 |
53 | // this is not a real id, so only text which can be visible is Delete
54 | expect(listItems[0].textContent).toBe('Delete')
55 | })
56 | })
57 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/config.js:
--------------------------------------------------------------------------------
1 | export const firebase = {
2 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3 | authDomain: 'redux-firebasev3.firebaseapp.com',
4 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
5 | projectId: 'redux-firebasev3',
6 | storageBucket: 'redux-firebasev3.appspot.com',
7 | messagingSenderId: '823357791673'
8 | }
9 |
10 | export const rrfConfig = {
11 | userProfile: 'users',
12 | useFirestoreForProfile: true, // Store in Firestore instead of Real Time DB
13 | enableLogging: false
14 | }
15 |
16 | export default { firebase, rrfConfig }
17 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 | import * as serviceWorker from './serviceWorker'
6 |
7 | ReactDOM.render( , document.getElementById('root'))
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister()
13 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { firebaseReducer } from 'react-redux-firebase'
3 | import { firestoreReducer } from 'redux-firestore'
4 |
5 | const rootReducer = combineReducers({
6 | firebase: firebaseReducer,
7 | firestore: firestoreReducer
8 | })
9 |
10 | export default rootReducer
11 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect'
6 |
--------------------------------------------------------------------------------
/examples/complete/firestore/src/store.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, createStore, compose } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import rootReducer from './reducer'
4 | import { getFirebase } from 'react-redux-firebase'
5 |
6 | export default function configureStore(initialState, history) {
7 | const middleware = [thunk.withExtraArgument({ getFirebase })]
8 | const createStoreWithMiddleware = compose(
9 | applyMiddleware(...middleware),
10 | typeof window === 'object' &&
11 | typeof window.devToolsExtension !== 'undefined'
12 | ? () => window.__REDUX_DEVTOOLS_EXTENSION__
13 | : f => f
14 | )(createStore)
15 | const store = createStoreWithMiddleware(rootReducer)
16 |
17 | if (module.hot) {
18 | // Enable Webpack hot module replacement for reducers
19 | module.hot.accept('./reducer', () => {
20 | const nextRootReducer = require('./reducer')
21 | store.replaceReducer(nextRootReducer)
22 | })
23 | }
24 |
25 | return store
26 | }
27 |
--------------------------------------------------------------------------------
/examples/complete/material/.env.local:
--------------------------------------------------------------------------------
1 | CI=false
2 | # Needed to skip warnings from jest@beta in package.json
3 | SKIP_PREFLIGHT_CHECK=true
4 |
--------------------------------------------------------------------------------
/examples/complete/material/.eslintignore:
--------------------------------------------------------------------------------
1 | **/coverage/**
2 | **/node_modules/**
3 | dist/**
4 | build/**
5 | src/index.html
6 | blueprints/**
7 | src/config.js
8 |
--------------------------------------------------------------------------------
/examples/complete/material/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': ['airbnb', 'airbnb/hooks', 'prettier', 'prettier/react'],
3 | root: true,
4 | parser: 'babel-eslint',
5 | plugins: ['import', 'babel', 'react', 'react-hooks', 'prettier'],
6 | settings: {
7 | react: {
8 | version: '16.9'
9 | },
10 | 'import/resolver': {
11 | node: {
12 | moduleDirectory: ['node_modules', '/']
13 | }
14 | }
15 | },
16 | rules: {
17 | semi: [
18 | 2, 'never'
19 | ],
20 | 'no-console': 'error',
21 | 'react/forbid-prop-types': 0,
22 | 'react/require-default-props': 0,
23 | 'react/jsx-filename-extension': 0,
24 | 'import/no-named-as-default': 0,
25 | 'no-return-await': 2,
26 | 'react-hooks/rules-of-hooks': 'error',
27 | 'react-hooks/exhaustive-deps': 'warn',
28 | 'prettier/prettier': [
29 | 'error',
30 | {
31 | singleQuote: true,
32 | trailingComma: 'none',
33 | semi: false,
34 | bracketSpacing: true,
35 | jsxBracketSameLine: true,
36 | printWidth: 80,
37 | tabWidth: 2,
38 | useTabs: false
39 | }
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/complete/material/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "redux-firebasev3",
4 | "master": "redux-firebasev3",
5 | "prod": "redux-firebasev3"
6 | },
7 | "ci": {
8 | "createConfig": {
9 | "master": {
10 | "env": "staging",
11 | "firebase": {
12 | "apiKey": "${STAGE_FIREBASE_API_KEY}",
13 | "authDomain": "redux-firebasev3.firebaseapp.com",
14 | "databaseURL": "https://redux-firebasev3.firebaseio.com",
15 | "projectId": "redux-firebasev3",
16 | "storageBucket": "redux-firebasev3.appspot.com"
17 | }
18 | },
19 | "prod": {
20 | "env": "production",
21 | "firebase": {
22 | "apiKey": "${PROD_FIREBASE_API_KEY}",
23 | "authDomain": "redux-firebasev3.firebaseapp.com",
24 | "databaseURL": "https://redux-firebasev3.firebaseio.com",
25 | "projectId": "redux-firebasev3",
26 | "storageBucket": "redux-firebasev3.appspot.com"
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/complete/material/.gitignore:
--------------------------------------------------------------------------------
1 | # OS
2 | *.log
3 | **/*.log
4 | .DS_Store
5 | **/.DS_Store
6 |
7 | # Dependencies
8 | **/node_modules
9 |
10 | # React App
11 | build
12 | src/config.js
13 | .env
14 |
--------------------------------------------------------------------------------
/examples/complete/material/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-react-firebase": {
3 | "promptValues": {
4 | "githubUser": "prescottprue",
5 | "firebaseName": "redux-firebasev3",
6 | "firebaseKey": "AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots",
7 | "includeRedux": true,
8 | "includeFirestore": false,
9 | "otherFeatures": [
10 | "Config for Continuous Integration"
11 | ],
12 | "ciProvider": "gitlab",
13 | "deployTo": "firebase"
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/examples/complete/material/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 - present prescottprue
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/complete/material/database.rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | ".read": "auth !== null",
4 | ".write":"auth !== null",
5 | "users": {
6 | "$uid": {
7 | ".write": "auth !== null && $uid === auth.uid"
8 | }
9 | },
10 | "projects": {
11 | ".indexOn": ["createdBy"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/complete/material/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "rules": "database.rules.json"
4 | },
5 | "storage": {
6 | "rules": "storage.rules"
7 | },
8 | "hosting": {
9 | "public": "build",
10 | "ignore": [
11 | "firebase.json",
12 | "**/.*",
13 | "**/node_modules/**",
14 | "jsconfig.json",
15 | "cypress/**"
16 | ],
17 | "rewrites": [
18 | {
19 | "source": "**",
20 | "destination": "/index.html"
21 | }
22 | ]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/complete/material/firestore.indexes.json:
--------------------------------------------------------------------------------
1 | {
2 | // Example:
3 | //
4 | // "indexes": [
5 | // {
6 | // "collectionId": "widgets",
7 | // "fields": [
8 | // { "fieldPath": "foo", "mode": "ASCENDING" },
9 | // { "fieldPath": "bar", "mode": "DESCENDING" }
10 | // ]
11 | // }
12 | // ]
13 | "indexes": []
14 | }
15 |
--------------------------------------------------------------------------------
/examples/complete/material/firestore.rules:
--------------------------------------------------------------------------------
1 | service cloud.firestore {
2 | match /databases/{database}/documents {
3 | function isAuthed() {
4 | return request.auth.uid != null
5 | }
6 | function isOwner(res) {
7 | return res.data.createdBy == request.auth.uid
8 | }
9 |
10 | // Private user profiles
11 | match /users/{userId} {
12 | allow read;
13 | allow write: if request.auth.uid == userId;
14 | }
15 |
16 | // Public user profiles
17 | match /users_public/{userId} {
18 | allow read;
19 | allow write: if false; // only written to by indexUser cloud function
20 | }
21 |
22 | // Projects
23 | match /projects/{projectId} {
24 | // Only projects you own can be viewed
25 | allow read, write: if isOwner(request.resource);
26 | // Rules apply to all child collections
27 | match /{allChildren=**} {
28 | allow read, write: if isOwner(get(/databases/$(database)/documents/projects/$(projectId)));
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/complete/material/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src",
4 | "paths": {
5 | "utils/*": [
6 | "utils/*"
7 | ]
8 | }
9 | },
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/examples/complete/material/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/material/public/favicon.ico
--------------------------------------------------------------------------------
/examples/complete/material/public/humans.txt:
--------------------------------------------------------------------------------
1 | # Check it out: http://humanstxt.org/
2 |
3 | # TEAM
4 |
5 | -- --
6 |
7 | # THANKS
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/complete/material/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Material",
3 | "name": "Material",
4 | "start_url": "/login/",
5 | "background_color": "#2196f3",
6 | "theme_color": "#2196f3",
7 | "display": "standalone"
8 | }
--------------------------------------------------------------------------------
/examples/complete/material/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/FormTextField/FormTextField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import TextField from '@material-ui/core/TextField'
4 |
5 | function FormTextField({
6 | label,
7 | input,
8 | meta: { touched, invalid, error },
9 | ...custom
10 | }) {
11 | return (
12 |
20 | )
21 | }
22 |
23 | FormTextField.propTypes = {
24 | formTextField: PropTypes.object
25 | }
26 |
27 | export default FormTextField
28 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/FormTextField/index.js:
--------------------------------------------------------------------------------
1 | import FormTextField from './FormTextField'
2 |
3 | export default FormTextField
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/LoadingSpinner/LoadingSpinner.enhancer.js:
--------------------------------------------------------------------------------
1 | import { withStyles } from '@material-ui/core/styles'
2 | import styles from './LoadingSpinner.styles'
3 |
4 | export default withStyles(styles)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/LoadingSpinner/LoadingSpinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import CircularProgress from '@material-ui/core/CircularProgress'
4 | import { makeStyles } from '@material-ui/core/styles'
5 | import styles from './LoadingSpinner.styles'
6 |
7 | const useStyles = makeStyles(styles)
8 |
9 | function LoadingSpinner({ size }) {
10 | const classes = useStyles()
11 |
12 | return (
13 |
18 | )
19 | }
20 |
21 | LoadingSpinner.propTypes = {
22 | size: PropTypes.number
23 | }
24 |
25 | export default LoadingSpinner
26 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/LoadingSpinner/LoadingSpinner.styles.js:
--------------------------------------------------------------------------------
1 | export default () => ({
2 | root: {
3 | display: 'flex',
4 | alignItems: 'center',
5 | flexDirection: 'column',
6 | justifyContent: 'flex-start',
7 | paddingTop: '7rem',
8 | height: '100%'
9 | },
10 | progress: {
11 | display: 'flex',
12 | justifyContent: 'center',
13 | alignItems: 'center',
14 | height: '50%'
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/LoadingSpinner/LoadingSpinner.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import renderer from 'react-test-renderer'
4 | import LoadingSpinner from './index'
5 |
6 | it('renders without crashing', () => {
7 | const div = document.createElement('div')
8 | ReactDOM.render( , div)
9 | ReactDOM.unmountComponentAtNode(div)
10 | })
11 |
12 | it('renders a spinner', () => {
13 | const tree = renderer.create( ).toJSON()
14 | expect(tree).toMatchSnapshot()
15 | })
16 |
--------------------------------------------------------------------------------
/examples/complete/material/src/components/LoadingSpinner/index.js:
--------------------------------------------------------------------------------
1 | import LoadingSpinner from './LoadingSpinner'
2 |
3 | export default LoadingSpinner
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NOTE: This file is ignored from git tracking. In a CI environment it is
3 | * generated by firebase-ci based on config in .firebaserc (see .gitlab-ci.yaml).
4 | * This is done so that environment specific settings can be applied.
5 | */
6 |
7 | export const env = 'local'
8 |
9 | // Config for firebase
10 | export const firebase = {
11 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
12 | authDomain: 'redux-firebasev3.firebaseapp.com',
13 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
14 | projectId: 'redux-firebasev3',
15 | storageBucket: 'redux-firebasev3.appspot.com'
16 | }
17 |
18 | // Config to override default reduxFirebase config in store/createStore
19 | // which is not environment specific.
20 | // For more details, visit http://react-redux-firebase.com/docs/api/enhancer.html
21 | export const reduxFirebase = {
22 | enableLogging: false, // enable/disable Firebase Database Logging
23 | }
24 |
25 | export default {
26 | env,
27 | firebase,
28 | reduxFirebase
29 | }
30 |
--------------------------------------------------------------------------------
/examples/complete/material/src/constants/formNames.js:
--------------------------------------------------------------------------------
1 | export const ACCOUNT_FORM_NAME = 'account'
2 | export const LOGIN_FORM_NAME = 'login'
3 | export const SIGNUP_FORM_NAME = 'signup'
4 | export const NEW_PROJECT_FORM_NAME = 'newProject'
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/constants/paths.js:
--------------------------------------------------------------------------------
1 | export const LIST_PATH = '/projects'
2 | export const ACCOUNT_PATH = '/account'
3 | export const LOGIN_PATH = '/login'
4 | export const SIGNUP_PATH = '/signup'
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/App/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { BrowserRouter as Router } from 'react-router-dom'
4 | import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
5 | import firebase from 'firebase/app'
6 | import 'firebase/auth'
7 | import 'firebase/database'
8 | import 'firebase/firestore' // make sure you add this for firestore
9 | import { ReactReduxFirebaseProvider } from 'react-redux-firebase'
10 | import { createFirestoreInstance } from 'redux-firestore'
11 | import { Provider } from 'react-redux'
12 | import ThemeSettings from 'theme'
13 | import { firebase as fbConfig, reduxFirebase as rfConfig } from 'config'
14 |
15 | const theme = createMuiTheme(ThemeSettings)
16 |
17 | // Initialize Firebase instance
18 | firebase.initializeApp(fbConfig)
19 |
20 | function App({ routes, store }) {
21 | return (
22 |
23 |
24 |
29 | {routes}
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | App.propTypes = {
37 | routes: PropTypes.object.isRequired,
38 | store: PropTypes.object.isRequired
39 | }
40 |
41 | export default App
42 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/App/index.js:
--------------------------------------------------------------------------------
1 | import App from './App'
2 |
3 | export default App
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/Navbar/LoginMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import Button from '@material-ui/core/Button'
4 | import { LOGIN_PATH, SIGNUP_PATH } from 'constants/paths'
5 |
6 | const buttonStyle = {
7 | color: 'white',
8 | textDecoration: 'none',
9 | alignSelf: 'center'
10 | }
11 |
12 | function LoginMenu() {
13 | return (
14 |
15 |
16 | Sign Up
17 |
18 |
19 | Login
20 |
21 |
22 | )
23 | }
24 |
25 | export default LoginMenu
26 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import AppBar from '@material-ui/core/AppBar'
4 | import Toolbar from '@material-ui/core/Toolbar'
5 | import Typography from '@material-ui/core/Typography'
6 | import Button from '@material-ui/core/Button'
7 | import { makeStyles } from '@material-ui/core/styles'
8 | import { useSelector } from 'react-redux'
9 | import { isLoaded, isEmpty } from 'react-redux-firebase'
10 | import { LIST_PATH, LOGIN_PATH } from 'constants/paths'
11 | import AccountMenu from './AccountMenu'
12 | import styles from './Navbar.styles'
13 |
14 | const useStyles = makeStyles(styles)
15 |
16 | function Navbar() {
17 | const classes = useStyles()
18 |
19 | // Get auth from redux state
20 | const auth = useSelector(state => state.firebase.auth)
21 | const authExists = isLoaded(auth) && !isEmpty(auth)
22 |
23 | return (
24 |
25 |
26 |
33 | material example
34 |
35 |
36 | {authExists ? (
37 |
38 | ) : (
39 |
44 | Sign In
45 |
46 | )}
47 |
48 |
49 | )
50 | }
51 |
52 | export default Navbar
53 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/Navbar/Navbar.styles.js:
--------------------------------------------------------------------------------
1 | export default () => ({
2 | flex: {
3 | flexGrow: 1
4 | },
5 | appBar: {
6 | // backgroundColor: theme.palette.primary1Color // Update this to change navbar color
7 | },
8 | signIn: {
9 | color: 'white',
10 | textDecoration: 'none',
11 | alignSelf: 'center'
12 | }
13 | })
14 |
--------------------------------------------------------------------------------
/examples/complete/material/src/containers/Navbar/index.js:
--------------------------------------------------------------------------------
1 | import Navbar from './Navbar'
2 |
3 | export default Navbar
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 |
5 | html,
6 | body {
7 | margin: 0;
8 | padding: 0;
9 | height: 100%;
10 | background-color: #F2F2F2;
11 | font-family: 'Roboto', 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
12 | }
13 | a {
14 | text-decoration: none !important;
15 | }
16 | *,
17 | *:before,
18 | *:after {
19 | box-sizing: inherit;
20 | }
21 | /* [type=button]{
22 | -webkit-appearance: none !important;
23 | } */
24 |
--------------------------------------------------------------------------------
/examples/complete/material/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { initScripts } from './utils'
4 | import createStore from './store/createStore'
5 | import { version } from '../package.json'
6 | import { env } from './config'
7 | import App from './containers/App'
8 | import './index.css'
9 |
10 | // import * as serviceWorker from './serviceWorker'
11 |
12 | // Window Variables
13 | // ------------------------------------
14 | window.version = version
15 | window.env = env
16 | initScripts()
17 |
18 | // Store Initialization
19 | // ------------------------------------
20 | const initialState = window.___INITIAL_STATE__ || {
21 | firebase: { authError: null }
22 | }
23 | const store = createStore(initialState)
24 | const routes = require('./routes/index').default(store)
25 |
26 | ReactDOM.render(
27 | ,
28 | document.getElementById('root')
29 | )
30 |
31 | // If you want your app to work offline and load faster, you can change
32 | // unregister() to register() below. Note this comes with some pitfalls.
33 | // Learn more about service workers: http://bit.ly/CRA-PWA
34 | // serviceWorker.unregister()
35 |
--------------------------------------------------------------------------------
/examples/complete/material/src/layouts/CoreLayout/CoreLayout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Navbar from 'containers/Navbar'
4 | import { Notifications } from 'modules/notification'
5 | import { makeStyles } from '@material-ui/core/styles'
6 | import styles from './CoreLayout.styles'
7 |
8 | const useStyles = makeStyles(styles)
9 |
10 | function CoreLayout({ children }) {
11 | const classes = useStyles()
12 |
13 | return (
14 |
15 |
16 |
{children}
17 |
18 |
19 | )
20 | }
21 |
22 | CoreLayout.propTypes = {
23 | children: PropTypes.element.isRequired
24 | }
25 |
26 | export default CoreLayout
27 |
--------------------------------------------------------------------------------
/examples/complete/material/src/layouts/CoreLayout/CoreLayout.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({})
2 |
--------------------------------------------------------------------------------
/examples/complete/material/src/layouts/CoreLayout/index.js:
--------------------------------------------------------------------------------
1 | import CoreLayout from './CoreLayout'
2 |
3 | export default CoreLayout
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/modules/notification/actionTypes.js:
--------------------------------------------------------------------------------
1 | export const NOTIFICATION_SHOW = 'NOTIFICATION_SHOW'
2 | export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS'
3 | export const NOTIFICATION_CLEAR = 'NOTIFICATION_CLEAR'
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/modules/notification/components/Notifications.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import Snackbar from '@material-ui/core/Snackbar'
5 | import IconButton from '@material-ui/core/IconButton'
6 | import CloseIcon from '@material-ui/icons/Close'
7 | import { makeStyles } from '@material-ui/core/styles'
8 | import * as actions from '../actions'
9 |
10 | const useStyles = makeStyles(() => ({
11 | buttonRoot: {
12 | color: 'white'
13 | }
14 | }))
15 |
16 | function Notifications({ allIds, byId, dismissNotification }) {
17 | const classes = useStyles()
18 |
19 | // Only render if notifications exist
20 | if (!allIds || !Object.keys(allIds).length) {
21 | return null
22 | }
23 |
24 | return (
25 |
26 | {allIds.map(id => (
27 | dismissNotification(id)}
33 | classes={{ root: classes.buttonRoot }}>
34 |
35 |
36 | }
37 | message={byId[id].message}
38 | />
39 | ))}
40 |
41 | )
42 | }
43 |
44 | Notifications.propTypes = {
45 | allIds: PropTypes.array.isRequired,
46 | byId: PropTypes.object.isRequired,
47 | dismissNotification: PropTypes.func.isRequired
48 | }
49 |
50 | export default connect(
51 | ({ notifications: { allIds, byId } }) => ({ allIds, byId }),
52 | actions
53 | )(Notifications)
54 |
--------------------------------------------------------------------------------
/examples/complete/material/src/modules/notification/components/useNotifications.js:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { bindActionCreators } from 'redux'
3 | import { useDispatch } from 'react-redux'
4 | import * as actions from '../actions'
5 |
6 | export default function useNotifications() {
7 | const dispatch = useDispatch()
8 | return useMemo(() => {
9 | return bindActionCreators(actions, dispatch)
10 | }, [dispatch])
11 | }
--------------------------------------------------------------------------------
/examples/complete/material/src/modules/notification/index.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import reducer from './reducer'
3 | import useNotifications from './components/useNotifications'
4 | import Notifications from './components/Notifications'
5 | import * as actionTypes from './actionTypes'
6 |
7 | export default actions
8 | export { reducer, useNotifications, Notifications, actionTypes }
9 |
--------------------------------------------------------------------------------
/examples/complete/material/src/modules/notification/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { without, omit } from 'lodash'
3 | import { NOTIFICATION_SHOW, NOTIFICATION_DISMISS } from './actionTypes'
4 |
5 | function notification(state = {}, action) {
6 | switch (action.type) {
7 | case NOTIFICATION_SHOW:
8 | return action.payload
9 | case NOTIFICATION_DISMISS:
10 | return undefined
11 | default:
12 | return state
13 | }
14 | }
15 |
16 | function allIds(state = [], action) {
17 | switch (action.type) {
18 | case NOTIFICATION_SHOW:
19 | return [...state, action.payload.id]
20 | case NOTIFICATION_DISMISS:
21 | return without(state, action.payload)
22 | default:
23 | return state
24 | }
25 | }
26 |
27 | function byId(state = {}, action) {
28 | switch (action.type) {
29 | case NOTIFICATION_SHOW:
30 | return {
31 | ...state,
32 | [action.payload.id]: notification(state[action.payload.id], action)
33 | }
34 | case NOTIFICATION_DISMISS:
35 | return omit(state, action.payload)
36 | default:
37 | return state
38 | }
39 | }
40 |
41 | export const notifications = combineReducers({ byId, allIds })
42 |
43 | export default notifications
44 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.enhancer.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { compose, setPropTypes } from 'recompose'
3 | import { reduxForm } from 'redux-form'
4 | import { ACCOUNT_FORM_NAME } from 'constants/formNames'
5 |
6 | export default compose(
7 | // set proptypes used in HOCs
8 | setPropTypes({
9 | onSubmit: PropTypes.func.isRequired // used by reduxForm
10 | }),
11 | // Add form capabilities
12 | reduxForm({ form: ACCOUNT_FORM_NAME })
13 | )
14 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | justifyContent: 'flex-start',
5 | width: '100%',
6 | height: '100%'
7 | },
8 | fields: {
9 | width: '60%'
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountForm/index.js:
--------------------------------------------------------------------------------
1 | import AccountForm from './AccountForm'
2 | import enhance from './AccountForm.enhancer'
3 |
4 | export default enhance(AccountForm)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountPage/AccountPage.enhancer.js:
--------------------------------------------------------------------------------
1 | import { UserIsAuthenticated } from 'utils/router'
2 |
3 | export default UserIsAuthenticated // redirect to /login if user is not authenticated
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountPage/AccountPage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexRowCenter,
4 | width: '100%',
5 | height: '100%',
6 | paddingTop: '1.5rem'
7 | },
8 | pane: {
9 | ...theme.flexColumnCenter,
10 | justifyContent: 'space-around',
11 | flexBasis: '60%',
12 | padding: theme.spacing()
13 | },
14 | settings: {
15 | ...theme.flexRowCenter
16 | },
17 | avatarCurrent: {
18 | width: '100%',
19 | maxWidth: '13rem',
20 | marginTop: '3rem',
21 | height: 'auto',
22 | cursor: 'pointer'
23 | },
24 | meta: {
25 | ...theme.flexColumnCenter,
26 | flexBasis: '60%',
27 | marginBottom: '3rem',
28 | marginTop: '2rem'
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/AccountPage/index.js:
--------------------------------------------------------------------------------
1 | import AccountPage from './AccountPage'
2 | import enhance from './AccountPage.enhancer'
3 |
4 | export default enhance(AccountPage)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/ProviderDataForm/ProviderDataForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import List from '@material-ui/core/List'
4 | import ListItem from '@material-ui/core/ListItem'
5 | import ListItemIcon from '@material-ui/core/ListItemIcon'
6 | import ListItemText from '@material-ui/core/ListItemText'
7 | import ListSubheader from '@material-ui/core/ListSubheader'
8 | import AccountCircle from '@material-ui/icons/AccountCircle'
9 |
10 | function ProviderData({ providerData }) {
11 | return (
12 | Accounts}>
13 | {providerData.map(providerAccount => (
14 |
15 |
16 |
17 |
18 |
19 |
20 | ))}
21 |
22 | )
23 | }
24 |
25 | ProviderData.propTypes = {
26 | providerData: PropTypes.array.isRequired
27 | }
28 |
29 | export default ProviderData
30 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/components/ProviderDataForm/index.js:
--------------------------------------------------------------------------------
1 | import ProviderDataForm from './ProviderDataForm'
2 |
3 | export default ProviderDataForm
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Account/index.js:
--------------------------------------------------------------------------------
1 | import { Loadable } from 'utils/components'
2 | import { ACCOUNT_PATH as path } from 'constants/paths'
3 |
4 | export default {
5 | path,
6 | component: Loadable({
7 | loader: () =>
8 | import(/* webpackChunkName: 'Account' */ './components/AccountPage')
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Home/components/HomePage/HomePage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter
4 | },
5 | section: {
6 | ...theme.flexColumnCenter
7 | }
8 | })
9 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Home/components/HomePage/index.js:
--------------------------------------------------------------------------------
1 | import HomePage from './HomePage'
2 |
3 | export default HomePage
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Home/index.js:
--------------------------------------------------------------------------------
1 | import HomePage from './components/HomePage'
2 |
3 | // Sync route definition
4 | export default {
5 | path: '/',
6 | component: HomePage
7 | }
8 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.enhancer.js:
--------------------------------------------------------------------------------
1 | import { reduxForm } from 'redux-form'
2 | import { LOGIN_FORM_NAME } from 'constants/formNames'
3 |
4 | // Add Form Capabilities
5 | export default reduxForm({ form: LOGIN_FORM_NAME })
6 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import { Field } from 'redux-form'
5 | import TextField from 'components/FormTextField'
6 | import Button from '@material-ui/core/Button'
7 | import { required, validateEmail } from 'utils/form'
8 | import styles from './LoginForm.styles'
9 |
10 | const useStyles = makeStyles(styles)
11 |
12 | function LoginForm({ pristine, submitting, handleSubmit }) {
13 | const classes = useStyles()
14 |
15 | return (
16 |
42 | )
43 | }
44 |
45 | LoginForm.propTypes = {
46 | pristine: PropTypes.bool.isRequired, // from enhancer (reduxForm)
47 | submitting: PropTypes.bool.isRequired, // from enhancer (reduxForm)
48 | handleSubmit: PropTypes.func.isRequired // from enhancer (reduxForm - calls onSubmit)
49 | }
50 |
51 | export default LoginForm
52 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | justifyContent: 'flex-start',
5 | flexGrow: 1,
6 | height: '100%',
7 | width: '100%',
8 | margin: '.2rem'
9 | },
10 | submit: {
11 | ...theme.flexColumnCenter,
12 | justifyContent: 'center',
13 | flexGrow: 1,
14 | textAlign: 'center',
15 | padding: '1.25rem',
16 | minWidth: '192px',
17 | marginTop: '1.5rem'
18 | }
19 | })
20 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginForm/index.js:
--------------------------------------------------------------------------------
1 | import LoginForm from './LoginForm'
2 | import enhance from './LoginForm.enhancer'
3 |
4 | export default enhance(LoginForm)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginPage/LoginPage.enhancer.js:
--------------------------------------------------------------------------------
1 | import { UserIsNotAuthenticated } from 'utils/router'
2 |
3 | // redirect to /projects if user is already authed
4 | export default UserIsNotAuthenticated
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginPage/LoginPage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | justifyContent: 'flex-start',
5 | height: '100%',
6 | width: '100%',
7 | fontWeight: 400,
8 | paddingTop: '1.5rem'
9 | },
10 | panel: {
11 | ...theme.flexColumnCenter,
12 | justifyContent: 'center',
13 | flexGrow: 1,
14 | padding: '1.25rem',
15 | minWidth: '250px',
16 | minHeight: '270px'
17 | },
18 | orLabel: {
19 | marginTop: '1rem',
20 | marginBottom: '.5rem'
21 | },
22 | signup: {
23 | ...theme.flexColumnCenter,
24 | justifyContent: 'center',
25 | marginTop: '2rem'
26 | },
27 | signupLabel: {
28 | fontSize: '1rem',
29 | fontWeight: 'bold'
30 | },
31 | signupLink: {
32 | fontSize: '1.2rem'
33 | },
34 | providers: {
35 | marginTop: '1rem'
36 | }
37 | })
38 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/components/LoginPage/index.js:
--------------------------------------------------------------------------------
1 | import LoginPage from './LoginPage'
2 | import enhance from './LoginPage.enhancer'
3 |
4 | export default enhance(LoginPage)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Login/index.js:
--------------------------------------------------------------------------------
1 | import { Loadable } from 'utils/components'
2 | import { LOGIN_PATH as path } from 'constants/paths'
3 |
4 | export default {
5 | path,
6 | component: Loadable({
7 | loader: () =>
8 | import(/* webpackChunkName: 'Login' */ './components/LoginPage')
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/NotFound/components/NotFoundPage/NotFoundPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Typography from '@material-ui/core/Typography'
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import styles from './NotFoundPage.styles'
5 |
6 | const useStyles = makeStyles(styles)
7 |
8 | function NotFoundPage() {
9 | const classes = useStyles()
10 |
11 | return (
12 |
13 |
Whoops! 404!
14 |
This page was not found.
15 |
16 | )
17 | }
18 |
19 | export default NotFoundPage
20 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/NotFound/components/NotFoundPage/NotFoundPage.styles.js:
--------------------------------------------------------------------------------
1 | export default () => ({
2 | root: {
3 | display: 'flex',
4 | flexDirection: 'column',
5 | alignItems: 'center'
6 | }
7 | })
8 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/NotFound/components/NotFoundPage/index.js:
--------------------------------------------------------------------------------
1 | import NotFoundPage from './NotFoundPage'
2 |
3 | export default NotFoundPage
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/NotFound/index.js:
--------------------------------------------------------------------------------
1 | import Loadable from 'react-loadable'
2 | import LoadingSpinner from 'components/LoadingSpinner'
3 |
4 | export default {
5 | component: Loadable({
6 | loader: () =>
7 | import(/* webpackChunkName: 'NotFound' */ './components/NotFoundPage'),
8 | loading: LoadingSpinner
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.enhancer.js:
--------------------------------------------------------------------------------
1 | import { reduxForm } from 'redux-form'
2 | import { NEW_PROJECT_FORM_NAME } from 'constants/formNames'
3 |
4 | export default reduxForm({
5 | form: NEW_PROJECT_FORM_NAME,
6 | // Clear the form for future use (creating another project)
7 | onSubmitSuccess: (result, dispatch, props) => props.reset()
8 | })
9 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | padding: theme.spacing(2)
4 | },
5 | inputs: {
6 | ...theme.flexColumnCenter
7 | },
8 | buttons: {
9 | ...theme.flexColumnCenter
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectDialog/index.js:
--------------------------------------------------------------------------------
1 | import NewProjectDialog from './NewProjectDialog'
2 | import enhance from './NewProjectDialog.enhancer'
3 |
4 | export default enhance(NewProjectDialog)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ContentAddCircle from '@material-ui/icons/AddCircle'
4 | import Paper from '@material-ui/core/Paper'
5 | import { makeStyles } from '@material-ui/core/styles'
6 | import styles from './NewProjectTile.styles'
7 |
8 | const useStyles = makeStyles(styles)
9 |
10 | function NewProjectTile({ onClick }) {
11 | const classes = useStyles()
12 |
13 | return (
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | NewProjectTile.propTypes = {
21 | onClick: PropTypes.func
22 | }
23 |
24 | export default NewProjectTile
25 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexRowCenter,
4 | alignItems: 'center',
5 | cursor: 'pointer',
6 | height: '200px',
7 | width: '300px',
8 | margin: theme.spacing(0.5),
9 | padding: theme.spacing(1.3),
10 | overflow: 'hidden'
11 | },
12 | newIcon: {
13 | width: '6rem',
14 | height: '6rem',
15 | transition: 'all 800ms cubic-bezier(0.25,0.1,0.25,1) 0ms',
16 | '&:hover': {
17 | color: '#757575'
18 | }
19 | }
20 | })
21 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/NewProjectTile/index.js:
--------------------------------------------------------------------------------
1 | import NewProjectTile from './NewProjectTile'
2 |
3 | export default NewProjectTile
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/ProjectTile/ProjectTile.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | display: 'flex',
4 | flexDirection: 'column',
5 | alignItems: 'flex-start',
6 | height: '200px',
7 | width: '300px',
8 | margin: theme.spacing(0.5),
9 | padding: theme.spacing(1.3)
10 | },
11 | top: {
12 | display: 'flex',
13 | justifyContent: 'space-between',
14 | width: '100%'
15 | },
16 | name: {
17 | fontSize: '1.5rem',
18 | cursor: 'pointer',
19 | textDecoration: 'none',
20 | transition: 'all 800ms cubic-bezier(0.25,0.1,0.25,1) 0ms',
21 | textOverflow: 'ellipsis',
22 | overflow: 'hidden',
23 | whiteSpace: 'nowrap',
24 | '&:hover': {
25 | color: ''
26 | },
27 | '&:visited': {
28 | textDecoration: 'none'
29 | }
30 | }
31 | })
32 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/ProjectTile/index.js:
--------------------------------------------------------------------------------
1 | import ProjectTile from './ProjectTile'
2 |
3 | export default ProjectTile
4 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/ProjectsPage/ProjectsPage.enhancer.js:
--------------------------------------------------------------------------------
1 | import { UserIsAuthenticated } from 'utils/router'
2 |
3 | // redirect to /login if user is not logged in
4 | export default UserIsAuthenticated
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/ProjectsPage/ProjectsPage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | paddingTop: theme.spacing(4),
5 | flexGrow: '2',
6 | boxSizing: 'border-box',
7 | overflowY: 'scroll'
8 | },
9 | tiles: {
10 | display: 'flex',
11 | justifyContent: 'center',
12 | flexWrap: 'wrap',
13 | '-webkit-flex-flow': 'row wrap'
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/components/ProjectsPage/index.js:
--------------------------------------------------------------------------------
1 | import ProjectsPage from './ProjectsPage'
2 | import enhance from './ProjectsPage.enhancer'
3 |
4 | export default enhance(ProjectsPage)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/index.js:
--------------------------------------------------------------------------------
1 | import { LIST_PATH as path } from 'constants/paths'
2 | import { Loadable } from 'utils/components'
3 |
4 | export default {
5 | path,
6 | component: Loadable({
7 | loader: () =>
8 | import(/* webpackChunkName: 'Projects' */ './components/ProjectsPage')
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/routes/Project/components/ProjectPage/ProjectPage.enhancer.js:
--------------------------------------------------------------------------------
1 | import { compose } from 'redux'
2 | import { withRouter } from 'react-router-dom'
3 | import { setDisplayName } from 'recompose'
4 | import { UserIsAuthenticated } from 'utils/router'
5 |
6 | export default compose(
7 | // Set component display name (more clear in dev/error tools)
8 | setDisplayName('EnhancedProjectPage'),
9 | // Add props.match
10 | withRouter,
11 | // Redirect to /login if user is not logged in
12 | UserIsAuthenticated,
13 | )
14 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/routes/Project/components/ProjectPage/ProjectPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Card from '@material-ui/core/Card'
3 | import CardContent from '@material-ui/core/CardContent'
4 | import Typography from '@material-ui/core/Typography'
5 | import { makeStyles } from '@material-ui/core/styles'
6 | import { useFirebaseConnect, isLoaded } from 'react-redux-firebase'
7 | import { useParams } from 'react-router-dom'
8 | import { useSelector } from 'react-redux'
9 | import LoadingSpinner from 'components/LoadingSpinner'
10 | import styles from './ProjectPage.styles'
11 |
12 | const useStyles = makeStyles(styles)
13 |
14 | function ProjectPage() {
15 | const { projectId } = useParams()
16 | const classes = useStyles()
17 |
18 | // Create listener for projects
19 | useFirebaseConnect(() => [{ path: `projects/${projectId}` }])
20 |
21 | // Get projects from redux state
22 | const project = useSelector(({ firebase: { data } }) => {
23 | return data.projects && data.projects[projectId]
24 | })
25 |
26 | // Show loading spinner while project is loading
27 | if (!isLoaded(project)) {
28 | return
29 | }
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | {project.name || 'Project'}
37 |
38 | {projectId}
39 |
40 |
{JSON.stringify(project, null, 2)}
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | export default ProjectPage
49 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/routes/Project/components/ProjectPage/ProjectPage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | padding: theme.spacing(2)
4 | },
5 | progress: {
6 | ...theme.flexRowCenter,
7 | alignItems: 'center',
8 | paddingTop: theme.spacing(8)
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/routes/Project/components/ProjectPage/index.js:
--------------------------------------------------------------------------------
1 | import ProjectPage from './ProjectPage'
2 | import enhance from './ProjectPage.enhancer'
3 |
4 | export default enhance(ProjectPage)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Projects/routes/Project/index.js:
--------------------------------------------------------------------------------
1 | import { Loadable } from 'utils/components'
2 |
3 | export default {
4 | path: ':projectId',
5 | component: Loadable({
6 | loader: () =>
7 | import(/* webpackChunkName: 'Project' */ './components/ProjectPage')
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.enhancer.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { reduxForm } from 'redux-form'
3 | import { setPropTypes, compose } from 'recompose'
4 | import { SIGNUP_FORM_NAME } from 'constants/formNames'
5 |
6 | export default compose(
7 | // Set prop-types used in HOCs
8 | setPropTypes({
9 | onSubmit: PropTypes.func.isRequired // called by handleSubmit
10 | }),
11 | // Add form capabilities (handleSubmit, pristine, submitting)
12 | reduxForm({
13 | form: SIGNUP_FORM_NAME
14 | })
15 | )
16 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | justifyContent: 'flex-start',
5 | flexGrow: 1,
6 | height: '100%',
7 | width: '100%',
8 | margin: '.2rem',
9 | fontSize: '1.4rem'
10 | },
11 | submit: {
12 | ...theme.flexColumnCenter,
13 | justifyContent: 'center',
14 | flexGrow: 1,
15 | textAlign: 'center',
16 | padding: '1.25rem',
17 | minWidth: '192px',
18 | marginTop: '1.5rem'
19 | }
20 | })
21 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupForm/index.js:
--------------------------------------------------------------------------------
1 | import SignupForm from './SignupForm'
2 | import enhance from './SignupForm.enhancer'
3 |
4 | export default enhance(SignupForm)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupPage/SignupPage.enhancer.js:
--------------------------------------------------------------------------------
1 | import { UserIsNotAuthenticated } from 'utils/router'
2 |
3 | // Redirect to list page if logged in
4 | export default UserIsNotAuthenticated
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupPage/SignupPage.styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | root: {
3 | ...theme.flexColumnCenter,
4 | justifyContent: 'flex-start',
5 | height: '100%',
6 | width: '100%',
7 | fontWeight: 400,
8 | paddingTop: '1.5rem'
9 | },
10 | panel: {
11 | ...theme.flexColumnCenter,
12 | justifyContent: 'center',
13 | flexGrow: 1,
14 | padding: '1.25rem',
15 | minWidth: '250px',
16 | minHeight: '270px'
17 | },
18 | orLabel: {
19 | marginTop: '1rem',
20 | marginBottom: '.5rem'
21 | },
22 | login: {
23 | ...theme.flexColumnCenter,
24 | justifyContent: 'center',
25 | marginTop: '1.5rem'
26 | },
27 | loginLabel: {
28 | fontSize: '1rem',
29 | fontWeight: 'bold'
30 | },
31 | loginLink: {
32 | fontSize: '1.2rem'
33 | },
34 | providers: {
35 | marginTop: '1rem'
36 | }
37 | })
38 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/components/SignupPage/index.js:
--------------------------------------------------------------------------------
1 | import SignupPage from './SignupPage'
2 | import enhance from './SignupPage.enhancer'
3 |
4 | export default enhance(SignupPage)
5 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/Signup/index.js:
--------------------------------------------------------------------------------
1 | import { Loadable } from 'utils/components'
2 | import { SIGNUP_PATH as path } from 'constants/paths'
3 |
4 | export default {
5 | path,
6 | component: Loadable({
7 | loader: () =>
8 | import(/* webpackChunkName: 'Signup' */ './components/SignupPage')
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/complete/material/src/routes/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route } from 'react-router-dom'
3 | import CoreLayout from '../layouts/CoreLayout'
4 | import Home from './Home'
5 | import LoginRoute from './Login'
6 | import SignupRoute from './Signup'
7 | import ProjectsRoute from './Projects'
8 | import AccountRoute from './Account'
9 | import NotFoundRoute from './NotFound'
10 |
11 | export default function createRoutes(store) {
12 | return (
13 |
14 |
15 | } />
16 | {/* Build Route components from routeSettings */
17 | [
18 | AccountRoute,
19 | ProjectsRoute,
20 | SignupRoute,
21 | LoginRoute
22 | /* Add More Routes Here */
23 | ].map((settings, index) => (
24 |
25 | ))}
26 |
27 |
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/examples/complete/material/src/static/User.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/material/src/static/User.png
--------------------------------------------------------------------------------
/examples/complete/material/src/store/createStore.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, compose, createStore } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import { getFirebase } from 'react-redux-firebase'
4 | import makeRootReducer from './reducers'
5 |
6 | export default (initialState = {}) => {
7 | // ======================================================
8 | // Store Enhancers
9 | // ======================================================
10 | const enhancers = []
11 |
12 | if (window && window.location && window.location.hostname === 'localhost') {
13 | const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__
14 | if (typeof devToolsExtension === 'function') {
15 | enhancers.push(devToolsExtension())
16 | }
17 | }
18 |
19 | // ======================================================
20 | // Middleware Configuration
21 | // ======================================================
22 | const middleware = [
23 | thunk.withExtraArgument(getFirebase)
24 | // This is where you add other middleware like redux-observable
25 | ]
26 |
27 | // ======================================================
28 | // Store Instantiation and HMR Setup
29 | // ======================================================
30 | const store = createStore(
31 | makeRootReducer(),
32 | initialState,
33 | compose(
34 | applyMiddleware(...middleware),
35 | ...enhancers
36 | )
37 | )
38 |
39 | store.asyncReducers = {}
40 |
41 | if (module.hot) {
42 | module.hot.accept('./reducers', () => {
43 | const reducers = require('./reducers').default // eslint-disable-line global-require
44 | store.replaceReducer(reducers(store.asyncReducers))
45 | })
46 | }
47 |
48 | return store
49 | }
50 |
--------------------------------------------------------------------------------
/examples/complete/material/src/store/location.js:
--------------------------------------------------------------------------------
1 | // ------------------------------------
2 | // Constants
3 | // ------------------------------------
4 | export const LOCATION_CHANGE = 'LOCATION_CHANGE'
5 |
6 | // ------------------------------------
7 | // Actions
8 | // ------------------------------------
9 | export function locationChange(location = '/') {
10 | return {
11 | type: LOCATION_CHANGE,
12 | payload: location
13 | }
14 | }
15 |
16 | // ------------------------------------
17 | // Specialized Action Creator
18 | // ------------------------------------
19 | export function updateLocation({ dispatch }) {
20 | return nextLocation => dispatch(locationChange(nextLocation))
21 | }
22 |
23 | // ------------------------------------
24 | // Reducer
25 | // ------------------------------------
26 | const initialState = null
27 | export default function locationReducer(state = initialState, action) {
28 | return action.type === LOCATION_CHANGE ? action.payload : state
29 | }
30 |
--------------------------------------------------------------------------------
/examples/complete/material/src/store/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { reducer as firebase } from 'react-redux-firebase'
3 | import { reducer as form } from 'redux-form'
4 | import { reducer as notifications } from 'modules/notification'
5 | import locationReducer from './location'
6 |
7 | export function makeRootReducer(asyncReducers) {
8 | return combineReducers({
9 | // Add sync reducers here
10 | firebase,
11 | form,
12 | notifications,
13 | location: locationReducer,
14 | ...asyncReducers
15 | })
16 | }
17 |
18 | export function injectReducer(store, { key, reducer }) {
19 | store.asyncReducers[key] = reducer // eslint-disable-line no-param-reassign
20 | store.replaceReducer(makeRootReducer(store.asyncReducers))
21 | }
22 |
23 | export default makeRootReducer
24 |
--------------------------------------------------------------------------------
/examples/complete/material/src/theme.js:
--------------------------------------------------------------------------------
1 | export default {
2 | palette: {
3 | primary: {
4 | main: '#2196f3'
5 | }
6 | },
7 | // Enable typography v2: https://material-ui.com/style/typography/#migration-to-typography-v2
8 | typography: {
9 | useNextVariants: true
10 | },
11 | flexColumnCenter: {
12 | display: 'flex',
13 | flexDirection: 'column',
14 | alignItems: 'center'
15 | },
16 | flexRowCenter: {
17 | display: 'flex',
18 | flexDirection: 'row',
19 | justifyContent: 'center'
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/complete/material/src/utils/form.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns error message if value does not exist, otherwise returns
3 | * undefined
4 | * @param {string} value - Email to validate
5 | * @returns {string|undefined} Required string if value is undefined
6 | * @example Required Field
7 | *
14 | */
15 | export function required(value) {
16 | return value ? undefined : 'Required'
17 | }
18 |
19 | /**
20 | * Returns error message if value is not a valid email, otherwise returns
21 | * undefined
22 | * @param {string} value - Email to validate
23 | * @returns {string|undefined} Required string if value is undefined
24 | * @example Basic
25 | *
31 | */
32 | export function validateEmail(value) {
33 | return value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
34 | ? 'Invalid email address'
35 | : undefined
36 | }
37 |
--------------------------------------------------------------------------------
/examples/complete/material/src/utils/hooks.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux'
2 | import { useDispatch } from 'react-redux'
3 | import { useMemo } from 'react'
4 |
5 | export function useActions(actions, deps) {
6 | const dispatch = useDispatch()
7 | return useMemo(() => {
8 | if (Array.isArray(actions)) {
9 | return actions.map(a => bindActionCreators(a, dispatch))
10 | }
11 | return bindActionCreators(actions, dispatch)
12 | }, deps ? [dispatch, ...deps] : [dispatch])
13 | }
--------------------------------------------------------------------------------
/examples/complete/material/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Log a message and return data passed. Useful for logging
3 | * messages within functional programming flows.
4 | * @param message - Message to log along with data.
5 | * @example Basic
6 | * import { flow, map as fpMap } from 'lodash'
7 | * const original = []
8 | * flow(
9 | * fpLog('Before Map'),
10 | * fpMap('name')
11 | * fpLog('After Map'),
12 | * )(original)
13 | * // => 'Before Map' [{ name: 'test' }]
14 | * // => 'After Map' ['test']
15 | */
16 | export function fpLog(message) {
17 | return existing => {
18 | console.log(message, existing) // eslint-disable-line no-console
19 | return existing
20 | }
21 | }
22 |
23 | /**
24 | * Initialize global scripts including analytics and error handling
25 | */
26 | export function initScripts() {
27 | // Initialize global scripts here
28 | }
29 |
--------------------------------------------------------------------------------
/examples/complete/material/storage.rules:
--------------------------------------------------------------------------------
1 | // Only authenticated users can read or write to the bucket
2 | service firebase.storage {
3 | match /b/{bucket}/o {
4 | match /{allPaths=**} {
5 | allow read, write: if request.auth != null;
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "babel-preset-react-native-stage-0/decorator-support"
4 | ],
5 | "env": {
6 | "development": {
7 | "plugins": [
8 | "transform-react-jsx-source"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | export default class App extends React.Component {
5 | render() {
6 | return (
7 |
8 | Open up App.js to start working on your app!
9 | Changes you make will automatically reload.
10 | Shake your phone to open the developer menu.
11 |
12 | );
13 | }
14 | }
15 |
16 | const styles = StyleSheet.create({
17 | container: {
18 | flex: 1,
19 | backgroundColor: '#fff',
20 | alignItems: 'center',
21 | justifyContent: 'center',
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from './App';
3 |
4 | import renderer from 'react-test-renderer';
5 |
6 | it('renders without crashing', () => {
7 | const rendered = renderer.create( ).toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/README.md:
--------------------------------------------------------------------------------
1 | # React Native Firebase Example
2 |
3 | > Simple example of using native modules with react-redux-firebase by using react-native-firebase
4 |
5 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
6 |
7 |
8 | ## Get Started
9 |
10 | 1. `yarn install`
11 | 1. `react-native run-ios`
12 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "20.0.0"
4 | },
5 | "name": "reactnativefirebase",
6 | "displayName": "RNF Example"
7 | }
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/index.android.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 | import App from './src';
3 | AppRegistry.registerComponent('reactnativefirebase', () => App);
4 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/index.ios.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 | import App from './src';
3 | AppRegistry.registerComponent('reactnativefirebase', () => App);
4 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AD_UNIT_ID_FOR_BANNER_TEST
6 | ca-app-pub-3940256099942544/2934735716
7 | AD_UNIT_ID_FOR_INTERSTITIAL_TEST
8 | ca-app-pub-3940256099942544/4411468910
9 | CLIENT_ID
10 | 823357791673-bup2vc9puame4ufum2s6b9qu5pr1ui1m.apps.googleusercontent.com
11 | REVERSED_CLIENT_ID
12 | com.googleusercontent.apps.823357791673-bup2vc9puame4ufum2s6b9qu5pr1ui1m
13 | API_KEY
14 | AIzaSyCDBrha6FJ5KER3j7bE3SXSfkV6cezOoMk
15 | GCM_SENDER_ID
16 | 823357791673
17 | PLIST_VERSION
18 | 1
19 | BUNDLE_ID
20 | com.react-redux-firebase.example
21 | PROJECT_ID
22 | redux-firebasev3
23 | STORAGE_BUCKET
24 | redux-firebasev3.appspot.com
25 | IS_ADS_ENABLED
26 |
27 | IS_ANALYTICS_ENABLED
28 |
29 | IS_APPINVITE_ENABLED
30 |
31 | IS_GCM_ENABLED
32 |
33 | IS_SIGNIN_ENABLED
34 |
35 | GOOGLE_APP_ID
36 | 1:823357791673:ios:ba3aefbcea3dadaa
37 | DATABASE_URL
38 | https://redux-firebasev3.firebaseio.com
39 |
40 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '10.0'
3 |
4 | target 'reactnativefirebase' do
5 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
6 | # use_frameworks!
7 |
8 | # Pods for reactnativefirebase
9 | # Required by RNFirebase
10 | pod 'Firebase/Core'
11 | pod 'RNFirebase', :path => '../node_modules/react-native-firebase'
12 |
13 | # [OPTIONAL PODS] - comment out pods for firebase products you won't be using.
14 | pod 'Firebase/AdMob'
15 | pod 'Firebase/Analytics'
16 | pod 'Firebase/Auth'
17 | pod 'Firebase/Crash'
18 | pod 'Firebase/Database'
19 | pod 'Firebase/DynamicLinks'
20 | pod 'Firebase/Messaging'
21 | pod 'Firebase/RemoteConfig'
22 | pod 'Firebase/Storage'
23 | pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
24 | pod 'React', :path => '../node_modules/react-native', :subspecs => [
25 | 'BatchedBridge', # Required For React Native 0.45.0+
26 | 'Core',
27 | # Add any other subspecs you want to use in your project
28 | ]
29 | end
30 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebase.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebase/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (nonatomic, strong) UIWindow *window;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebase/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 |
12 | #import
13 | #import
14 | #import
15 |
16 | @implementation AppDelegate
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
19 | {
20 | NSURL *jsCodeLocation;
21 |
22 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
23 |
24 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
25 | moduleName:@"reactnativefirebase"
26 | initialProperties:nil
27 | launchOptions:launchOptions];
28 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
29 |
30 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
31 | UIViewController *rootViewController = [UIViewController new];
32 | rootViewController.view = rootView;
33 | self.window.rootViewController = rootViewController;
34 | [self.window makeKeyAndVisible];
35 | [FIRApp configure];
36 | return YES;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebase/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebase/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "AppDelegate.h"
13 |
14 | int main(int argc, char * argv[]) {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/ios/reactnativefirebaseTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-firebase-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "babel-preset-react-native-stage-0": "^1.0.1",
7 | "jest-expo": "~20.0.0",
8 | "react-test-renderer": "16.0.0-alpha.12"
9 | },
10 | "scripts": {
11 | "start": "react-native start",
12 | "android": "react-native run-android",
13 | "ios": "react-native run-ios",
14 | "test": "node node_modules/jest/bin/jest.js --watch"
15 | },
16 | "jest": {
17 | "preset": "jest-expo"
18 | },
19 | "dependencies": {
20 | "react": "16.0.0-alpha.12",
21 | "react-native": "^0.47.0",
22 | "react-native-firebase": "^2.2.3",
23 | "react-redux": "^5.0.6",
24 | "react-redux-firebase": "next",
25 | "recompose": "^0.26.0",
26 | "redux": "^3.7.2",
27 | "redux-thunk": "^2.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/src/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { compose } from 'redux'
3 | import { connect } from 'react-redux'
4 | import { isLoaded, isEmpty, firebaseConnect } from 'react-redux-firebase'
5 | import {
6 | View,
7 | Text,
8 | StyleSheet,
9 | ActivityIndicator,
10 | TouchableOpacity
11 | } from 'react-native'
12 | import NewTodo from './NewTodo'
13 | import TodosList from './TodosList'
14 |
15 | const enhnace = compose(
16 | connect(({ firebase: { auth } }) => ({
17 | uid: auth.uid
18 | })),
19 | withStateHandlers(({ initialText = '' }) => ({ newTodoText: initialText }), {
20 | onEmailChange: () => email => ({ email }),
21 | onPasswordChange: () => password => ({ password }),
22 | onNewTodoChange: () => newTodoText => ({ newTodoText })
23 | }),
24 | withHandlers({
25 | addTodo: props => () => {
26 | const newTodo = { text: newTodoText || 'Sample Text', owner: props.uid }
27 | return props.firebase.push('todos', newTodo)
28 | }
29 | })
30 | )
31 |
32 | const Home = ({ todos, addTodo, onNewTodoChange, newTodoText }) => (
33 |
34 | Todos
35 |
40 |
41 |
42 | )
43 |
44 | export default enhance(Home)
45 |
46 | const styles = StyleSheet.create({
47 | container: {
48 | marginTop: 100,
49 | paddingLeft: 15,
50 | paddingRight: 15
51 | },
52 | header: {
53 | alignSelf: 'center',
54 | marginBottom: 50
55 | }
56 | })
57 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/src/NewTodo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | View,
4 | Text,
5 | StyleSheet,
6 | TextInput,
7 | TouchableOpacity
8 | } from 'react-native';
9 |
10 | const NewTodo = ({ newValue, onInputChange, onNewTouch }) => (
11 |
12 |
17 |
18 | Add Todo
19 |
20 |
21 | )
22 |
23 | export default NewTodo;
24 |
25 | const styles = StyleSheet.create({
26 | button: {
27 | height: 50,
28 | marginBottom: 40,
29 | padding: 10,
30 | borderWidth: 1,
31 | borderColor: 'black',
32 | backgroundColor: '#cccccc',
33 | alignItems: 'center',
34 | justifyContent: 'center',
35 | alignSelf: 'center',
36 | },
37 | input: {
38 | height: 40,
39 | backgroundColor: '#fafafa',
40 | borderColor: 'grey',
41 | borderBottomWidth: 1,
42 | paddingLeft: 15,
43 | paddingRight: 15
44 | }
45 | });
46 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/src/createStore.js:
--------------------------------------------------------------------------------
1 | import { compose, createStore } from 'redux'
2 | import { reactReduxFirebase } from 'react-redux-firebase'
3 | import RNFirebase from 'react-native-firebase'
4 | import makeRootReducer from './reducers'
5 |
6 | const reactNativeFirebaseConfig = {
7 | debug: true
8 | }
9 | // for more config options, visit http://docs.react-redux-firebase.com/history/v2.0.0/docs/api/compose.html
10 | const reduxFirebaseConfig = {
11 | userProfile: 'users' // save users profiles to 'users' collection
12 | }
13 |
14 | export default (initialState = { firebase: {} }) => {
15 | // initialize firebase
16 | const firebase = RNFirebase.initializeApp(reactNativeFirebaseConfig)
17 |
18 | const store = createStore(
19 | makeRootReducer(),
20 | initialState, // initial state
21 | compose(
22 | reactReduxFirebase(firebase, reduxFirebaseConfig) // pass initialized react-native-firebase app instance
23 | )
24 | )
25 | return store
26 | }
27 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import createStore from './createStore';
4 | import Home from './Home';
5 |
6 | // Store Initialization
7 | const initialState = { firebase: {} };
8 | const store = createStore(initialState);
9 |
10 | const Main = () => (
11 |
12 |
13 |
14 | );
15 |
16 | export default Main;
17 |
--------------------------------------------------------------------------------
/examples/complete/react-native-firebase/src/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { firebaseStateReducer } from 'react-redux-firebase'
3 |
4 | export const makeRootReducer = (asyncReducers) => {
5 | return combineReducers({
6 | // Add sync reducers here
7 | firebase: firebaseStateReducer,
8 | ...asyncReducers
9 | })
10 | }
11 |
12 | export default makeRootReducer
13 |
14 | // Useful for injecting reducers as part of async routes
15 | export const injectReducer = (store, { key, reducer }) => {
16 | store.asyncReducers[key] = reducer
17 | store.replaceReducer(makeRootReducer(store.asyncReducers))
18 | }
19 |
--------------------------------------------------------------------------------
/examples/complete/react-native/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | globals: {
3 | __DEV__: false
4 | }
5 | }
--------------------------------------------------------------------------------
/examples/complete/react-native/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p12
6 | *.key
7 | *.mobileprovision
8 |
--------------------------------------------------------------------------------
/examples/complete/react-native/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/examples/complete/react-native/__tests__/App-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import App from '../App';
4 | import renderer from 'react-test-renderer';
5 | import NavigationTestUtils from 'react-navigation/NavigationTestUtils';
6 |
7 | describe('App snapshot', () => {
8 | jest.useFakeTimers();
9 | beforeEach(() => {
10 | NavigationTestUtils.resetInternalState();
11 | });
12 |
13 | it('renders the loading screen', async () => {
14 | const tree = renderer.create( ).toJSON();
15 | expect(tree).toMatchSnapshot();
16 | });
17 |
18 | it('renders the root without loading screen', async () => {
19 | const tree = renderer.create( ).toJSON();
20 | expect(tree).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/examples/complete/react-native/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-redux-firebase example",
4 | "slug": "react-redux-firebase-example",
5 | "privacy": "public",
6 | "sdkVersion": "32.0.0",
7 | "platforms": [
8 | "ios",
9 | "android"
10 | ],
11 | "version": "1.0.0",
12 | "orientation": "portrait",
13 | "icon": "./assets/images/icon.png",
14 | "splash": {
15 | "image": "./assets/images/splash.png",
16 | "resizeMode": "contain",
17 | "backgroundColor": "#ffffff"
18 | },
19 | "updates": {
20 | "fallbackToCacheTimeout": 0
21 | },
22 | "assetBundlePatterns": [
23 | "**/*"
24 | ],
25 | "ios": {
26 | "supportsTablet": true
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/examples/complete/react-native/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/react-native/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/examples/complete/react-native/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/react-native/assets/images/icon.png
--------------------------------------------------------------------------------
/examples/complete/react-native/assets/images/robot-dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/react-native/assets/images/robot-dev.png
--------------------------------------------------------------------------------
/examples/complete/react-native/assets/images/robot-prod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/react-native/assets/images/robot-prod.png
--------------------------------------------------------------------------------
/examples/complete/react-native/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/react-native/assets/images/splash.png
--------------------------------------------------------------------------------
/examples/complete/react-native/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/examples/complete/react-native/components/StyledText.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text } from 'react-native';
3 |
4 | export class MonoText extends React.Component {
5 | render() {
6 | return ;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/complete/react-native/components/TabBarIcon.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Icon } from 'expo';
3 |
4 | import Colors from '../constants/Colors';
5 |
6 | export default class TabBarIcon extends React.Component {
7 | render() {
8 | return (
9 |
15 | );
16 | }
17 | }
--------------------------------------------------------------------------------
/examples/complete/react-native/components/Todo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { StyleSheet, Text, TouchableOpacity, Button } from 'react-native'
4 | import { compose, withHandlers } from 'recompose'
5 | import { withFirebase } from 'react-redux-firebase'
6 | import { connect } from 'react-redux'
7 |
8 | const enhance = compose(
9 | connect(({ firebase }, { todoKey }) => {
10 | const { text, done } =
11 | (firebase.data.todos && firebase.data.todos[todoKey]) || {}
12 | return {
13 | done,
14 | text
15 | }
16 | }),
17 | withFirebase,
18 | withHandlers({
19 | toggleDone: props => () =>
20 | props.firebase.update(`todos/${props.todoKey}`, { done: !props.done })
21 | })
22 | )
23 |
24 | function Todo({ done, text, toggleDone }) {
25 | return (
26 |
27 |
28 | {text}
29 |
30 | )
31 | }
32 |
33 | Todo.propTypes = {
34 | text: PropTypes.string,
35 | done: PropTypes.bool,
36 | toggleDone: PropTypes.func
37 | }
38 |
39 | export default enhance(Todo)
40 |
41 | const styles = StyleSheet.create({
42 | helpLinkText: {
43 | fontSize: 25,
44 | color: '#2e78b7'
45 | },
46 | helpLink: {
47 | paddingVertical: 15,
48 | display: 'flex',
49 | flexDirection: 'row'
50 | }
51 | })
52 |
--------------------------------------------------------------------------------
/examples/complete/react-native/components/TodosList.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { StyleSheet, View } from 'react-native'
4 | import { connect } from 'react-redux'
5 | import { firebaseConnect } from 'react-redux-firebase'
6 | import { compose, withHandlers } from 'recompose'
7 | import Todo from './Todo'
8 |
9 | const withTodos = compose(
10 | firebaseConnect(() => [
11 | {
12 | path: 'todos',
13 | queryParams: ['limitToLast=10']
14 | }
15 | ]),
16 | connect(({ firebase }) => ({
17 | todos: firebase.ordered.todos
18 | })),
19 | withHandlers({
20 | addTodo: props => () =>
21 | props.firebase.push('todos', { text: 'sample', done: false })
22 | })
23 | )
24 |
25 | function Todos({ todos }) {
26 | return (
27 |
28 | {todos &&
29 | todos.map((todoItem, i) => )}
30 |
31 | )
32 | }
33 |
34 | Todos.propTypes = {
35 | todos: PropTypes.array
36 | }
37 |
38 | const styles = StyleSheet.create({
39 | helpContainer: {
40 | marginTop: 15,
41 | alignItems: 'center'
42 | }
43 | })
44 |
45 | export default withTodos(Todos)
46 |
--------------------------------------------------------------------------------
/examples/complete/react-native/components/__tests__/StyledText-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import { MonoText } from '../StyledText';
4 | import renderer from 'react-test-renderer';
5 |
6 | it('renders correctly', () => {
7 | const tree = renderer.create(Snapshot test! ).toJSON();
8 |
9 | expect(tree).toMatchSnapshot();
10 | });
11 |
--------------------------------------------------------------------------------
/examples/complete/react-native/config.js:
--------------------------------------------------------------------------------
1 | export const firebase = {
2 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3 | authDomain: 'redux-firebasev3.firebaseapp.com',
4 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
5 | projectId: 'redux-firebasev3',
6 | storageBucket: 'redux-firebasev3.appspot.com',
7 | messagingSenderId: '823357791673'
8 | }
9 |
10 | export const rrfConfig = {
11 | userProfile: 'users',
12 | useFirestoreForProfile: true, // Store in Firestore instead of Real Time DB
13 | enableLogging: false
14 | }
15 |
16 | export default { firebase, rrfConfig }
17 |
--------------------------------------------------------------------------------
/examples/complete/react-native/constants/Colors.js:
--------------------------------------------------------------------------------
1 | const tintColor = '#2f95dc';
2 |
3 | export default {
4 | tintColor,
5 | tabIconDefault: '#ccc',
6 | tabIconSelected: tintColor,
7 | tabBar: '#fefefe',
8 | errorBackground: 'red',
9 | errorText: '#fff',
10 | warningBackground: '#EAEB5E',
11 | warningText: '#666804',
12 | noticeBackground: tintColor,
13 | noticeText: '#fff',
14 | };
15 |
--------------------------------------------------------------------------------
/examples/complete/react-native/constants/Layout.js:
--------------------------------------------------------------------------------
1 | import { Dimensions } from 'react-native';
2 |
3 | const width = Dimensions.get('window').width;
4 | const height = Dimensions.get('window').height;
5 |
6 | export default {
7 | window: {
8 | width,
9 | height,
10 | },
11 | isSmallDevice: width < 375,
12 | };
13 |
--------------------------------------------------------------------------------
/examples/complete/react-native/navigation/AppNavigator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createAppContainer, createSwitchNavigator } from 'react-navigation';
3 |
4 | import MainTabNavigator from './MainTabNavigator';
5 |
6 | export default createAppContainer(createSwitchNavigator({
7 | // You could add another route here for authentication.
8 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html
9 | Main: MainTabNavigator,
10 | }));
--------------------------------------------------------------------------------
/examples/complete/react-native/navigation/MainTabNavigator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Platform } from 'react-native';
3 | import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
4 |
5 | import TabBarIcon from '../components/TabBarIcon';
6 | import HomeScreen from '../screens/HomeScreen';
7 | import LinksScreen from '../screens/LinksScreen';
8 | import SettingsScreen from '../screens/SettingsScreen';
9 |
10 | const HomeStack = createStackNavigator({
11 | Home: HomeScreen,
12 | });
13 |
14 | HomeStack.navigationOptions = {
15 | tabBarLabel: 'Home',
16 | tabBarIcon: ({ focused }) => (
17 |
25 | ),
26 | };
27 |
28 | const LinksStack = createStackNavigator({
29 | Links: LinksScreen,
30 | });
31 |
32 | LinksStack.navigationOptions = {
33 | tabBarLabel: 'Links',
34 | tabBarIcon: ({ focused }) => (
35 |
39 | ),
40 | };
41 |
42 | const SettingsStack = createStackNavigator({
43 | Settings: SettingsScreen,
44 | });
45 |
46 | SettingsStack.navigationOptions = {
47 | tabBarLabel: 'Settings',
48 | tabBarIcon: ({ focused }) => (
49 |
53 | ),
54 | };
55 |
56 | export default createBottomTabNavigator({
57 | HomeStack,
58 | LinksStack,
59 | SettingsStack,
60 | });
61 |
--------------------------------------------------------------------------------
/examples/complete/react-native/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "eject": "expo eject",
8 | "test": "node ./node_modules/jest/bin/jest.js --watchAll"
9 | },
10 | "jest": {
11 | "preset": "jest-expo"
12 | },
13 | "dependencies": {
14 | "@expo/samples": "2.1.1",
15 | "expo": "^32.0.0",
16 | "firebase": "^5.8.2",
17 | "prop-types": "^15.6.2",
18 | "react": "16.5.0",
19 | "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
20 | "react-navigation": "^3.0.9",
21 | "react-redux": "^5.0.0",
22 | "react-redux-firebase": "latest",
23 | "recompose": "^0.30.0",
24 | "redux": "^4.0.1"
25 | },
26 | "devDependencies": {
27 | "babel-preset-expo": "^5.0.0",
28 | "jest-expo": "^32.0.0"
29 | },
30 | "private": true
31 | }
32 |
--------------------------------------------------------------------------------
/examples/complete/react-native/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { firebaseStateReducer as firebase } from 'react-redux-firebase'
3 |
4 | const rootReducer = combineReducers({
5 | firebase
6 | })
7 |
8 | export default rootReducer
9 |
--------------------------------------------------------------------------------
/examples/complete/react-native/screens/LinksScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ScrollView, StyleSheet } from 'react-native';
3 | import { ExpoLinksView } from '@expo/samples';
4 |
5 | export default class LinksScreen extends React.Component {
6 | static navigationOptions = {
7 | title: 'Links',
8 | };
9 |
10 | render() {
11 | return (
12 |
13 | {/* Go ahead and delete ExpoLinksView and replace it with your
14 | * content, we just wanted to provide you with some helpful links */}
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 | const styles = StyleSheet.create({
22 | container: {
23 | flex: 1,
24 | paddingTop: 15,
25 | backgroundColor: '#fff',
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/examples/complete/react-native/screens/SettingsScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ExpoConfigView } from '@expo/samples';
3 |
4 | export default class SettingsScreen extends React.Component {
5 | static navigationOptions = {
6 | title: 'app.json',
7 | };
8 |
9 | render() {
10 | /* Go ahead and delete ExpoConfigView and replace it with your
11 | * content, we just wanted to give you a quick view of your config */
12 | return ;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/complete/react-native/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose } from 'redux'
2 | import firebase from 'firebase/app'
3 | import 'firebase/auth'
4 | import 'firebase/database'
5 | import 'firebase/firestore' // make sure you add this for firestore
6 | import { reactReduxFirebase } from 'react-redux-firebase'
7 | import { firebase as fbConfig } from './config'
8 | import rootReducer from './reducer'
9 |
10 | export default function configureStore(initialState, history) {
11 | // Initialize Firebase instance
12 | firebase.initializeApp(fbConfig)
13 |
14 | const createStoreWithMiddleware = compose(
15 | reactReduxFirebase(firebase, {
16 | userProfile: 'users',
17 | useFirestoreForProfile: true, // Store in Firestore instead of Real Time DB
18 | enableLogging: false
19 | })
20 | )(createStore)
21 |
22 | const store = createStoreWithMiddleware(rootReducer)
23 |
24 | if (module.hot) {
25 | // Enable Webpack hot module replacement for reducers
26 | module.hot.accept('./reducer', () => {
27 | const nextRootReducer = require('./reducer')
28 | store.replaceReducer(nextRootReducer)
29 | })
30 | }
31 |
32 | return store
33 | }
34 |
--------------------------------------------------------------------------------
/examples/complete/simple/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/examples/complete/simple/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "redux-firebasev3"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/complete/simple/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | .DS_Store
14 | npm-debug.log
15 |
16 | .firebase
--------------------------------------------------------------------------------
/examples/complete/simple/database.rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | ".read": true,
4 | ".write": true,
5 | "cars": {
6 | ".read": "auth !== null",
7 | ".write": "auth !== null"
8 | },
9 | "todos": {
10 | ".read": true,
11 | ".write": true
12 | },
13 | "projects": {
14 | ".indexOn": ["createdBy"],
15 | ".read": "auth !== null"
16 | },
17 | "users": {
18 | "Anonymous": {
19 | ".read": true
20 | },
21 | "$uid": {
22 | ".read": "auth !== null",
23 | ".write": "$uid === auth.uid"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/complete/simple/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "rules": "database.rules.json"
4 | },
5 | "hosting": {
6 | "public": "build",
7 | "ignore": [
8 | "firebase.json",
9 | "**/.*",
10 | "**/node_modules/**"
11 | ],
12 | "rewrites": [
13 | {
14 | "source": "**",
15 | "destination": "/index.html"
16 | }
17 | ]
18 | },
19 | "emulators": {
20 | "database": {
21 | "port": 9000
22 | },
23 | "firestore": {
24 | "port": 8080
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/complete/simple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-firebase-simple-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "react-scripts start",
7 | "build": "react-scripts build",
8 | "test": "react-scripts test",
9 | "eject": "react-scripts eject",
10 | "emulate": "firebase emulators:start --only firestore,database",
11 | "dev": "cross-env REACT_APP_USE_DB_EMULATORS=true yarn start"
12 | },
13 | "dependencies": {
14 | "firebase": "^7.8.2",
15 | "prop-types": "^15.7.2",
16 | "react": "16.12.0",
17 | "react-dom": "16.12.0",
18 | "react-redux": "^7.1.0",
19 | "react-redux-firebase": "*",
20 | "redux": "^4.0.4",
21 | "redux-firestore": "^0.12.0",
22 | "redux-thunk": "^2.3.0"
23 | },
24 | "devDependencies": {
25 | "cross-env": "^7.0.0",
26 | "react-scripts": "3.4.0"
27 | },
28 | "eslintConfig": {
29 | "extends": "react-app"
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/complete/simple/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/simple/public/favicon.ico
--------------------------------------------------------------------------------
/examples/complete/simple/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/complete/simple/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-redux-firebase simple example",
3 | "name": "Create React + react-redux-firebase App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | .App-Url {
22 | margin-left: 1rem;
23 | }
24 | .App-Car {
25 | margin: 1rem;
26 | }
27 |
28 |
29 | @keyframes App-logo-spin {
30 | from { transform: rotate(0deg); }
31 | to { transform: rotate(360deg); }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import firebase from 'firebase/app'
4 | import 'firebase/auth'
5 | import 'firebase/database'
6 | import 'firebase/firestore' // make sure you add this for firestore
7 | import { ReactReduxFirebaseProvider } from 'react-redux-firebase';
8 | import { createFirestoreInstance } from 'redux-firestore';
9 | import Home from './Home'
10 | import configureStore from './store'
11 | import initFirebase from './initFirebase'
12 | import { reduxFirebase as rfConfig } from './config'
13 | import './App.css'
14 |
15 | const initialState = window && window.__INITIAL_STATE__ // set initial state here
16 | const store = configureStore(initialState)
17 | // Initialize Firebase instance
18 | initFirebase()
19 |
20 | export default function App () {
21 | return (
22 |
23 |
28 |
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | });
9 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Todos from './Todos'
3 | import NewTodo from './NewTodo'
4 | import logo from './logo.svg'
5 | import './App.css'
6 |
7 | function Home() {
8 | return (
9 |
10 |
11 |
react-redux-firebase demo
12 |
13 |
14 |
15 |
23 |
Todos List
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default Home
32 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/NewTodo.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useFirebase } from 'react-redux-firebase'
3 |
4 | function NewTodo() {
5 | const [inputVal, changeInput] = useState('')
6 | const firebase = useFirebase()
7 |
8 | function resetInput() {
9 | changeInput('')
10 | }
11 | function onInputChange(e) {
12 | return changeInput(e && e.target && e.target.value)
13 | }
14 | function addTodo() {
15 | return firebase.push('todos', { text: inputVal || 'sample', done: false })
16 | }
17 |
18 | return (
19 |
20 |
New Todo
21 |
22 | Add
23 | Cancel
24 |
25 | )
26 | }
27 |
28 | export default NewTodo
29 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/Todo.css:
--------------------------------------------------------------------------------
1 | .Todo {
2 | margin: 1rem;
3 |
4 | }
5 | .Todo-Input {
6 | margin: 1rem;
7 | }
8 |
9 | .Todo-Button {
10 | margin: 1rem;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { useSelector } from 'react-redux'
4 | import { useFirebase } from 'react-redux-firebase'
5 | import './Todo.css'
6 |
7 | function TodoItem({ id }) {
8 | const todo = useSelector(state => state.firebase.data.todos[id])
9 | const firebase = useFirebase()
10 |
11 | function toggleDone() {
12 | firebase.update(`todos/${id}`, { done: !todo.done })
13 | }
14 | function deleteTodo() {
15 | return firebase.remove(`todos/${id}`)
16 | }
17 |
18 | return (
19 |
20 |
26 | {todo.text || todo.name}
27 |
28 | Delete
29 |
30 |
31 | )
32 | }
33 |
34 | TodoItem.propTypes = {
35 | id: PropTypes.string.isRequired
36 | }
37 |
38 | export default TodoItem
39 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/Todos.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from 'react-redux'
3 | import { useFirebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'
4 | import TodoItem from './TodoItem'
5 |
6 | const todosQuery = {
7 | path: 'todos',
8 | queryParams: ['limitToLast=10']
9 | }
10 |
11 | function Todos() {
12 | // Attach todos listener
13 | useFirebaseConnect(() => [
14 | todosQuery
15 | ])
16 |
17 | // Get todos from redux state
18 | const todos = useSelector(state => state.firebase.ordered.todos)
19 |
20 | // Show a message while todos are loading
21 | if (!isLoaded(todos)) {
22 | return 'Loading'
23 | }
24 |
25 | // Show a message if there are no todos
26 | if (isEmpty(todos)) {
27 | return 'Todo list is empty'
28 | }
29 |
30 | return todos.reverse().map(({ value: todo, key }, ind) => (
31 |
36 | ))
37 | }
38 |
39 | export default Todos
40 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/config.js:
--------------------------------------------------------------------------------
1 | export const firebase = {
2 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3 | authDomain: 'redux-firebasev3.firebaseapp.com',
4 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
5 | storageBucket: 'redux-firebasev3.appspot.com',
6 | messagingSenderId: '823357791673',
7 | projectId: 'redux-firebasev3'
8 | }
9 |
10 | export const reduxFirebase = {
11 | userProfile: 'users',
12 | useFirestoreForProfile: true,
13 | enableLogging: false
14 | }
15 |
16 | export default { firebase, reduxFirebase }
17 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/simple/src/favicon.ico
--------------------------------------------------------------------------------
/examples/complete/simple/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.render(
7 | ,
8 | document.getElementById('root')
9 | )
10 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/initFirebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/app'
2 | import 'firebase/auth'
3 | import 'firebase/database'
4 | import 'firebase/firestore' // make sure you add this for firestore
5 | import { firebase as fbConfig } from './config'
6 |
7 | let firebaseInstance
8 |
9 | export default function initFirebase(initialState, history) {
10 | if (firebaseInstance) {
11 | return firebaseInstance
12 | }
13 |
14 | // Initialize firebase instance if it doesn't already exist
15 | if (!firebaseInstance) {
16 | const shouldUseEmulator = process.env.REACT_APP_USE_DB_EMULATORS
17 |
18 | if (shouldUseEmulator) { // or window.location.hostname === 'localhost' if you want
19 | console.log('Using RTDB emulator')
20 | fbConfig.databaseURL = `http://localhost:9000?ns=${fbConfig.projectId}`
21 | }
22 |
23 | // Initialize Firebase instance
24 | firebase.initializeApp(fbConfig)
25 |
26 | if (shouldUseEmulator) { // or window.location.hostname === 'localhost' if you want
27 | console.log('Using Firestore emulator')
28 | firebase.firestore().settings({
29 | host: 'localhost:8080',
30 | ssl: false
31 | })
32 | }
33 | firebaseInstance = firebase
34 | }
35 |
36 |
37 | return firebaseInstance
38 | }
39 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { reducer as firebase } from 'react-redux-firebase'
3 | // import { reducer as firestore } from 'react-redux-firebase'
4 |
5 | const rootReducer = combineReducers({
6 | firebase,
7 | // firestore // add this for firestore
8 | })
9 |
10 | export default rootReducer
11 |
--------------------------------------------------------------------------------
/examples/complete/simple/src/store.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, createStore, compose } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import rootReducer from './reducer'
4 | import { getFirebase } from 'react-redux-firebase'
5 |
6 | export default function configureStore (initialState, history) {
7 | const middleware = [
8 | thunk.withExtraArgument({ getFirebase })
9 | ]
10 | const createStoreWithMiddleware = compose(
11 | applyMiddleware(...middleware),
12 | typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? () => window.__REDUX_DEVTOOLS_EXTENSION__ : f => f
13 | )(createStore)
14 | const store = createStoreWithMiddleware(rootReducer)
15 |
16 | if (module.hot) {
17 | // Enable Webpack hot module replacement for reducers
18 | module.hot.accept('./reducer', () => {
19 | const nextRootReducer = require('./reducer')
20 | store.replaceReducer(nextRootReducer)
21 | })
22 | }
23 |
24 | return store
25 | }
26 |
--------------------------------------------------------------------------------
/examples/complete/typescript/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/examples/complete/typescript/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | 'extends': [
4 | 'airbnb-base',
5 | 'prettier',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'prettier/@typescript-eslint',
8 | 'react-app',
9 | 'prettier',
10 | 'prettier/react'
11 | ],
12 | root: true,
13 | plugins: ['@typescript-eslint', 'import', 'babel', 'react', 'react-hooks', 'prettier' ],
14 | settings: {
15 | 'import/resolver': {
16 | node: {
17 | moduleDirectory: ['node_modules', '/'],
18 | extensions: ['.js', '.jsx', '.ts', '.tsx']
19 | }
20 | },
21 | react: {
22 | version: '16.0'
23 | }
24 | },
25 | env: {
26 | browser: true,
27 | node: true
28 | },
29 | rules: {
30 | '@typescript-eslint/no-explicit-any': 0,
31 | 'import/prefer-default-export': 0,
32 | 'no-shadow': 0,
33 | 'consistent-return': 0,
34 | 'no-new': 0,
35 | 'new-cap': 0,
36 | 'no-return-await': 2,
37 | 'import/extensions': 0,
38 | 'react-hooks/rules-of-hooks': 'error',
39 | 'react-hooks/exhaustive-deps': 'warn',
40 | 'prettier/prettier': [
41 | 'error',
42 | {
43 | singleQuote: true,
44 | trailingComma: 'none',
45 | semi: false,
46 | bracketSpacing: true,
47 | jsxBracketSameLine: true,
48 | printWidth: 80,
49 | tabWidth: 2,
50 | useTabs: false
51 | }
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/complete/typescript/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/examples/complete/typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-firebase-typescript-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "react-scripts start",
7 | "build": "react-scripts build",
8 | "test": "react-scripts test",
9 | "eject": "react-scripts eject",
10 | "lint": "eslint 'src/**/*.ts'",
11 | "lint:fix": "npm run lint -- --fix"
12 | },
13 | "dependencies": {
14 | "react": "^16.10.0",
15 | "react-dom": "^16.10.0",
16 | "react-redux": "^7.1.0",
17 | "react-redux-firebase": "next",
18 | "redux": "^4.0.4"
19 | },
20 | "devDependencies": {
21 | "@types/jest": "25.1.2",
22 | "@types/node": "13.7.0",
23 | "@types/react": "16.9.19",
24 | "@types/react-dom": "16.9.5",
25 | "@types/react-redux": "^7.1.7",
26 | "@typescript-eslint/eslint-plugin": "^2.19.0",
27 | "@typescript-eslint/parser": "^2.19.0",
28 | "eslint": "^6.8.0",
29 | "eslint-config-airbnb": "^18.0.1",
30 | "eslint-config-prettier": "^6.10.0",
31 | "eslint-plugin-babel": "^5.3.0",
32 | "eslint-plugin-import": "^2.20.1",
33 | "eslint-plugin-jsx-a11y": "^6.2.3",
34 | "eslint-plugin-prettier": "^3.1.2",
35 | "eslint-plugin-react": "^7.18.3",
36 | "eslint-plugin-react-hooks": "^2.3.0",
37 | "prettier": "^1.19.1",
38 | "react-scripts": "3.3.1",
39 | "typescript": "3.7.5"
40 | },
41 | "eslintConfig": {
42 | "extends": "react-app"
43 | },
44 | "browserslist": []
45 | }
46 |
--------------------------------------------------------------------------------
/examples/complete/typescript/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srsedev/react-redux-firebase/e67bbd71ab25fc4830d881431a1661a93b316efc/examples/complete/typescript/public/favicon.ico
--------------------------------------------------------------------------------
/examples/complete/typescript/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | React App
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/examples/complete/typescript/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/AddTodo.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ExtendedFirebaseInstance, useFirebase } from "react-redux-firebase";
3 |
4 | function AddTodo() {
5 | const firebase: ExtendedFirebaseInstance = useFirebase();
6 |
7 | function handleAddClick() {
8 | firebase.push("todos", { done: false, text: "Example todo" });
9 | }
10 | return (
11 |
12 | Add Todo
13 |
14 | );
15 | }
16 |
17 | export default AddTodo;
18 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | pointer-events: none;
9 | }
10 |
11 | .App-header {
12 | background-color: #282c34;
13 | min-height: 100vh;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | justify-content: center;
18 | font-size: calc(10px + 2vmin);
19 | color: white;
20 | }
21 |
22 | .App-link {
23 | color: #61dafb;
24 | }
25 |
26 | @keyframes App-logo-spin {
27 | from {
28 | transform: rotate(0deg);
29 | }
30 | to {
31 | transform: rotate(360deg);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/App.tsx:
--------------------------------------------------------------------------------
1 | import firebase from "firebase/app";
2 | import "firebase/auth";
3 | import "firebase/database";
4 | import "firebase/firestore"; // make sure you add this for firestore
5 | import React from "react";
6 | import { Provider } from "react-redux";
7 | import { ReactReduxFirebaseProvider } from "react-redux-firebase";
8 | import { firebase as fbConfig, reduxFirebase as rfConfig } from "./config";
9 | import Home from "./Home";
10 | import configureStore from "./store";
11 |
12 | const initialState = {};
13 | const store = configureStore(initialState);
14 | // Initialize Firebase instance
15 | firebase.initializeApp(fbConfig);
16 |
17 | export default () => (
18 |
19 |
23 |
24 |
25 |
26 | );
27 |
28 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/Home.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Todos from "./Todos";
3 | import AddTodo from "./AddTodo";
4 | import "./App.css";
5 | import logo from "./logo.svg";
6 |
7 | function Home() {
8 | return (
9 |
20 | );
21 | }
22 |
23 | export default Home;
24 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/Todo.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { AppState, TodoValue } from './reducer'
3 | import { useSelector } from "react-redux";
4 | import { useFirebase } from 'react-redux-firebase'
5 |
6 | interface TodoProps {
7 | todoId: string
8 | }
9 |
10 | function Todo({ todoId }: TodoProps) {
11 | const todo: TodoValue = useSelector((state: AppState) => {
12 | return state.firebase.data.todos && state.firebase.data.todos[todoId]
13 | })
14 | const firebase = useFirebase()
15 | function toggleDoneState() {
16 | firebase.update(`todos/${todoId}`, { done: !todo.done })
17 | }
18 |
19 | return (
20 |
21 |
22 | {todo.text}
23 |
24 | );
25 | }
26 |
27 | export default Todo;
28 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/Todos.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isLoaded, isEmpty, useFirebaseConnect } from "react-redux-firebase";
3 | import { AppState } from './reducer'
4 | import Todo from './Todo'
5 | import { useSelector } from "react-redux";
6 |
7 | function Todos() {
8 | useFirebaseConnect([{ path: 'todos', queryParams: ['limitToLast=10'] }])
9 | const todos = useSelector((state: AppState) => {
10 | return state.firebase.ordered.todos
11 | })
12 |
13 | if (!isLoaded(todos)) {
14 | return (
15 |
16 | Loading...
17 |
18 | );
19 | }
20 |
21 | if (isEmpty(todos)) {
22 | return (
23 |
24 | No Todos Found
25 |
26 | );
27 | }
28 |
29 | return (
30 |
31 | {
32 | todos && todos.map((todoItem) => {
33 | return
34 | })
35 | }
36 |
37 | );
38 | }
39 |
40 | export default Todos;
41 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/config.ts:
--------------------------------------------------------------------------------
1 | export const firebase = {
2 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3 | authDomain: 'redux-firebasev3.firebaseapp.com',
4 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
5 | storageBucket: 'redux-firebasev3.appspot.com',
6 | messagingSenderId: '823357791673',
7 | projectId: 'redux-firebasev3'
8 | }
9 |
10 | export const reduxFirebase = {
11 | userProfile: 'users',
12 | useFirestoreForProfile: true,
13 | enableLogging: false
14 | }
15 |
16 | export default { firebase, reduxFirebase }
17 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render( , document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | // /
2 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/reducer.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { firebaseReducer, FirebaseReducer } from 'react-redux-firebase'
3 |
4 | interface UserProfile {
5 | email: string
6 | }
7 |
8 | export interface TodoValue {
9 | text: string
10 | done: boolean
11 | }
12 |
13 | // create schema for the DB
14 | interface DBSchema {
15 | todos: TodoValue
16 | [name: string]: any
17 | }
18 | interface RootState {
19 | firebase: FirebaseReducer.Reducer
20 | // firestore: FirestoreReducer.Reducer;
21 | }
22 |
23 | const rootReducer = combineReducers({
24 | firebase: firebaseReducer
25 | })
26 |
27 | export type AppState = ReturnType
28 |
29 | export default rootReducer
30 |
--------------------------------------------------------------------------------
/examples/complete/typescript/src/store.ts:
--------------------------------------------------------------------------------
1 | import { compose, createStore } from 'redux'
2 | import rootReducer from './reducer'
3 |
4 | export default function configureStore(): any {
5 | const createStoreWithMiddleware = compose(
6 | typeof window === 'object' &&
7 | typeof (window as any).devToolsExtension !== 'undefined'
8 | ? (): any => (window as any).__REDUX_DEVTOOLS_EXTENSION__ // eslint-disable-line no-underscore-dangle
9 | : (f: any): any => f
10 | )(createStore)
11 |
12 | const store = createStoreWithMiddleware(rootReducer)
13 |
14 | if ((module as any).hot) {
15 | // Enable Webpack hot module replacement for reducers
16 | ;(module as any).hot.accept('./reducer', () => {
17 | const nextRootReducer = require('./reducer') // eslint-disable-line global-require, @typescript-eslint/no-var-requires
18 | store.replaceReducer(nextRootReducer)
19 | })
20 | }
21 |
22 | return store
23 | }
24 |
--------------------------------------------------------------------------------
/examples/complete/typescript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/examples/complete/typescript/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {},
7 | "rules": {
8 | "interface-name": false
9 | },
10 | "rulesDirectory": []
11 | }
--------------------------------------------------------------------------------
/examples/snippets/decorators/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { firebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'
5 | import TodoItem from './TodoItem'
6 |
7 | @firebaseConnect([
8 | 'todos'
9 | ])
10 | @connect(
11 | ({ firebase: { ordered } }) => ({
12 | todos: ordered.todos
13 | })
14 | )
15 | export default class App extends Component {
16 | static propTypes = {
17 | todos: PropTypes.array
18 | }
19 |
20 | render () {
21 | const { firebase, todos } = this.props
22 |
23 | const todosList = (!isLoaded(todos))
24 | ? 'Loading'
25 | : (isEmpty(todos))
26 | ? 'Todo list is empty'
27 | : todos.map((todo, id) => (
28 |
29 | ))
30 | return (
31 |
32 |
33 |
react-redux-firebase decorators demo
34 |
35 |
36 |
Todos List
37 | {todosList}
38 |
39 |
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/snippets/decorators/README.md:
--------------------------------------------------------------------------------
1 | # Decorators Example
2 |
3 | Snippet that shows usage of decorators. Similar to the [simple example](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/simple), this example shows syncing a list of todo items.
4 |
5 | ## Note
6 |
7 | This example is not a full application. For a full application please view the examples in the [Complete Folder](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete) including the [material example](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete/material).
8 |
--------------------------------------------------------------------------------
/examples/snippets/decorators/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { withFirebase } from 'react-redux-firebase'
4 |
5 | import './Todo.css'
6 |
7 | @withFirebase // pass down props.firebase (firebaseConnect() can also be used)
8 | export default class TodoItem extends Component {
9 | static propTypes = {
10 | todo: PropTypes.object,
11 | id: PropTypes.string
12 | }
13 |
14 | toggleDone = () => firebase.set(`/todos/${id}/done`, !todo.done)
15 |
16 | delete = (event) => firebase.remove(`/todos/${id}`)
17 |
18 | render(){
19 | const { todo, id } = this.props
20 |
21 | return (
22 |
23 |
29 | {todo.text}
30 |
31 | Delete
32 |
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/snippets/multipleQueries/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { map } from 'lodash'
4 | import { connect } from 'react-redux'
5 | import { compose } from 'redux'
6 | import { firebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'
7 | import TodoItem from './TodoItem'
8 |
9 | function renderList(list) {
10 | return !isLoaded(list)
11 | ? 'Loading'
12 | : isEmpty(list)
13 | ? 'Todo list is empty'
14 | : map(list, (todo, id) => )
15 | }
16 |
17 | function Home({ incompleteTodos, completeTodos }) {
18 | return (
19 |
20 |
21 |
react-redux-firebase multiple queries demo
22 |
23 |
24 |
Incomplete Todos
25 | {renderList(incompleteTodos)}
26 | Complete Todos
27 | {renderList(completeTodos)}
28 |
29 |
30 | )
31 | }
32 |
33 | Home.propTypes = {
34 | incompleteTodos: PropTypes.object,
35 | completeTodos: PropTypes.object
36 | }
37 |
38 | const enhance = compose(
39 | firebaseConnect([
40 | {
41 | path: 'todos',
42 | storeAs: 'incompleteTodos',
43 | queryParams: ['orderByChild=done', 'equalTo=true']
44 | },
45 | {
46 | path: 'todos',
47 | storeAs: 'completeTodos',
48 | queryParams: ['orderByChild=done', 'equalTo=false']
49 | }
50 | ]),
51 | connect(({ firebase: { data } }) => ({
52 | incompleteTodos: data['incompleteTodos'], // path matches storeAs
53 | completeTodos: data['completeTodos'] // path matches storeAs
54 | }))
55 | )
56 |
57 | export default enhance(Home)
58 |
--------------------------------------------------------------------------------
/examples/snippets/multipleQueries/README.md:
--------------------------------------------------------------------------------
1 | # Multiple Queries Example
2 |
3 | Snippet that shows usage of `storeAs` to make multiple queries to the same location on Firebase. Similar to the [simple example](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/simple), this example shows syncing a list of todo items.
4 |
5 | ## Note
6 |
7 | This example is not a full application. For a full application please view the examples in the [Complete Folder](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete) including the [material example](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete/material).
8 |
--------------------------------------------------------------------------------
/examples/snippets/multipleQueries/TodoItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { firebaseConnect } from 'react-redux-firebase'
4 | import './Todo.css'
5 |
6 | const TodoItem = ({ firebase, todo, id }) => {
7 | const toggleDone = () => firebase.set(`todos/${id}/done`, !todo.done)
8 | const deleteTodo = () => firebase.remove(`todos/${id}`)
9 | return (
10 |
11 |
17 | {todo.text}
18 |
19 | Delete
20 |
21 |
22 | )
23 | }
24 |
25 | TodoItem.propTypes = {
26 | todo: PropTypes.object,
27 | id: PropTypes.string
28 | }
29 |
30 | export default firebaseConnect()(TodoItem)
31 |
--------------------------------------------------------------------------------
/examples/snippets/populates/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { map } from 'lodash'
4 | import { compose } from 'redux'
5 | import { connect } from 'react-redux'
6 | import {
7 | firebaseConnect,
8 | populate,
9 | isLoaded,
10 | isEmpty
11 | } from 'react-redux-firebase'
12 |
13 | // NOTE: In real application don't forget to use Provider from react-redux
14 | // or firebaseConnect/withFirebase will not work
15 | function Projects({ projects }) {
16 | return (
17 |
18 |
react-redux-firebase populate snippet
19 |
20 |
Projects List
21 | {!isLoaded(projects)
22 | ? 'Loading'
23 | : isEmpty(projects)
24 | ? 'Todo list is empty'
25 | : map(projects, (project, id) => (
26 |
27 | Name: {project.name}
28 | Owner: {project.owner.displayName}
29 |
30 | ))}
31 |
32 |
33 | )
34 | }
35 |
36 | Projects.propTypes = {
37 | projects: PropTypes.object
38 | }
39 |
40 | const populates = [
41 | { child: 'owner', root: 'users' }
42 | // or if you want a param of the populate child such as user's display name
43 | // { child: 'owner', root: 'users', childParam: 'displayName' }
44 | ]
45 |
46 | const enhance = compose(
47 | // gather projects and matching owners from firebase and place into redux
48 | firebaseConnect(() => [{ path: 'projects', populates }]),
49 | // projects with owner populated from redux into component props
50 | connect(state => ({
51 | projects: populate(state.firebase, 'projects', populates)
52 | }))
53 | )
54 |
55 | export default enhance(Projects)
56 |
--------------------------------------------------------------------------------
/examples/snippets/populates/README.md:
--------------------------------------------------------------------------------
1 | # Populates Snippet
2 |
3 | This example shows using data from redux state to be used in queries. A good example of this is querying based on the current user's UID.
4 |
5 | **Note** Example does not use routing, which is what will commonly be used when creating a full application. For how to build with routing, please view [the routing recipes section of the docs.](https://react-redux-firebase.com/docs/recipes/routing.html)
6 |
7 | ## What It Does
8 |
9 | 1. Top level component uses `firebaseConnect` function to set a listener for the `projects` path on firebase. When the listener updates it also goes and gets the object from the `users` path that matches the owner from each project. This will be used in the next step, but for now the data has been loaded into redux.
10 |
11 | ```js
12 | const populates = [
13 | { child: 'owner', root: 'users' },
14 | ]
15 |
16 | firebaseConnect([
17 | { path: 'projects', populates },
18 | ])
19 | ```
20 |
21 | 1. Next is the `connect` HOC which allows us to grab from redux state and pass that data in as props to the component. In this case we are going to get projects with the owner parameter on each project replaced with the matching user:
22 |
23 | ```js
24 | connect((state) => ({
25 | projects: populate(state.firebase, 'projects', populates),
26 | }))
27 | ```
28 |
29 | 1. `isLoaded` can be used as usual to wait for data to be loaded. The projects list has the owner parameter "populated" from users.
30 |
--------------------------------------------------------------------------------
/examples/snippets/stateBasedQuery/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { isLoaded, isEmpty } from 'react-redux-firebase'
5 | import Todos from './Todos'
6 | import LoginView from './LoginView'
7 |
8 | function Home({ auth }) {
9 | // handle initial loading of auth
10 | if (!isLoaded(auth)) {
11 | return Loading...
12 | }
13 |
14 | // User is not logged in, show login view
15 | if (isEmpty(auth)) {
16 | return
17 | }
18 |
19 | return
20 | }
21 |
22 | Home.propTypes = {
23 | auth: PropTypes.object
24 | }
25 |
26 | export default connect(state => ({
27 | auth: state.firebase.auth
28 | }))(Home)
29 |
--------------------------------------------------------------------------------
/examples/snippets/stateBasedQuery/Todos.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { compose } from 'redux'
4 | import { connect } from 'react-redux'
5 | import { firebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'
6 | import TodoItem from './TodoItem'
7 |
8 | function Todos({ todos }) {
9 | return (
10 |
11 | {!isLoaded(todos)
12 | ? 'Loading'
13 | : isEmpty(todos)
14 | ? 'Todo list is empty'
15 | : todos.map(({ key }) => )}
16 |
17 | )
18 | }
19 |
20 | Todos.propTypes = {
21 | /* eslint-disable react/no-unused-prop-types */
22 | auth: PropTypes.shape({
23 | uid: PropTypes.string.isRequired
24 | }),
25 | /* eslint-enable react/no-unused-prop-types */
26 | todos: PropTypes.object
27 | }
28 |
29 | const enhance = compose(
30 | firebaseConnect(props => [
31 | // uid comes from props
32 | {
33 | path: 'todos',
34 | queryParams: ['orderByChild=uid', `equalTo=${props.uid}`]
35 | }
36 | ]),
37 | connect(state => ({
38 | todos: state.firebase.ordered.todos
39 | }))
40 | )
41 |
42 | export default enhance(Todos)
43 |
--------------------------------------------------------------------------------
/examples/snippets/watchEvent/Basic.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { compose } from 'redux'
4 | import { connect } from 'react-redux'
5 | import { isLoaded, isEmpty, withFirebase } from 'react-redux-firebase'
6 |
7 | class SomeThing extends PureComponent {
8 | static propTypes = {
9 | todos: PropTypes.object
10 | }
11 |
12 | componentWillMount () {
13 | this.props.firebase.watchEvent('value', 'todos')
14 | }
15 |
16 | componentWillUnMount () {
17 | this.props.firebase.unWatchEvent('value', 'todos')
18 | }
19 |
20 | render () {
21 | const { todos } = this.props
22 |
23 | if (!isLoaded(todos)) {
24 | return Loading...
25 | }
26 |
27 | if (isEmpty(todos)) {
28 | return No Todos Found
29 | }
30 |
31 | return {JSON.stringify(todos, null, 2)}
32 | }
33 | }
34 |
35 | export default compose(
36 | withFirebase, // add props.firebase
37 | connect(({ firebase: { data: { todos } } }) => ({
38 | todos // map todos from redux state to props
39 | })),
40 | )(SomeThing)
41 |
--------------------------------------------------------------------------------
/examples/snippets/watchEvent/README.md:
--------------------------------------------------------------------------------
1 | # watchEvent snippet
2 |
3 | This example shows creating a listener directly using `watchEvent`, which is handled automatically by `firebaseConnect`
4 |
5 | ## Whats Included
6 |
7 | - Basic snippet - Uses `watchEvent` directly
8 | - Recompose snippet - Uses `recompose` to simplify common patterns
9 |
10 | ## What It Does
11 |
12 | 1. Top level component uses `connect` function to bring `todos` from redux state into a prop name "todos":
13 |
14 | ```js
15 | export default compose(
16 | withFirebase, // add props.firebase
17 | connect(({ firebase: { data: { todos } } }) => ({
18 | todos // map todos from redux state to props
19 | }))
20 | )(SomeThing)
21 | ```
22 |
23 | 1. When the component mounts `watchEvent` is called to attach a listener for `todos` path of real time database
24 | 1. That component then uses `isLoaded` and `isEmpty` to show different views based on auth state (whether data is loaded or not):
25 |
26 | ```js
27 | render () {
28 | const { todos } = this.props
29 |
30 | if (!isLoaded(todos)) {
31 | return Loading...
32 | }
33 |
34 | if (isEmpty(todos)) {
35 | return No Todos Found
36 | }
37 |
38 | return {JSON.stringify(todos, null, 2)}
39 | }
40 | ```
41 |
42 | **NOTE**: This is simplified functionality of what is available through [`firebaseConnect`](https://react-redux-firebase.com/docs/api/firebaseConnect.html) and [`useFirebaseConnect`](https://react-redux-firebase.com/docs/api/useFirebaseConnect.html).
43 |
--------------------------------------------------------------------------------
/examples/snippets/watchEvent/Recompose.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { isLoaded, isEmpty, withFirebase } from 'react-redux-firebase'
5 | import { compose, lifecycle, pure } from 'recompose'
6 |
7 | const SomeThing = ({ todos }) => {
8 | if (!isLoaded(todos)) {
9 | return Loading...
10 | }
11 |
12 | if (isEmpty(todos)) {
13 | return No Todos Found
14 | }
15 |
16 | return {JSON.stringify(todos, null, 2)}
17 | }
18 |
19 | SomeThing.propTypes = {
20 | todos: PropTypes.object
21 | }
22 |
23 | // Create enhancer by composing HOCs
24 | const enhance = compose(
25 | withFirebase, // add props.firebase
26 | lifecycle({
27 | componentWillMount () {
28 | this.props.firebase.watchEvent('value', 'todos')
29 | },
30 | componentWillUnmount () {
31 | this.props.firebase.unWatchEvent('value', 'todos')
32 | }
33 | }),
34 | connect(({ firebase: { data: { todos } } }) => ({
35 | todos // map todos from redux state to props
36 | })),
37 | pure // shallowEqual comparison of props for rendering optimization
38 | )
39 |
40 | export default enhance(SomeThing)
41 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["es2015", {
4 | "es2015": {
5 | "loose": true,
6 | "modules": false
7 | }
8 | }], "react"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/README.md:
--------------------------------------------------------------------------------
1 | # Webpack 2 Example
2 |
3 | Snippet that shows usage `react-redux-firebase` in an application bundled with webpack 2/3
4 |
5 | ## Note
6 |
7 | This example is not a full application. For a full application please view the examples in the [Complete Folder](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete) including the [material example](https://github.com/prescottprue/react-redux-firebase/tree/master/examples/complete/material).
8 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-firebase-webpack2-example",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "react": "15.4.1",
6 | "react-dom": "15.4.1",
7 | "react-redux": "^5.0.5",
8 | "react-redux-firebase": "^1.2.5"
9 | },
10 | "devDependencies": {
11 | "babel-core": "6.18.2",
12 | "babel-loader": "6.2.8",
13 | "babel-preset-es2015": "6.18.0",
14 | "babel-preset-react": "6.16.0",
15 | "http-server": "0.9.0",
16 | "webpack": "^2.2.1"
17 | },
18 | "scripts": {
19 | "build": "webpack --config webpack.config.js",
20 | "start": "http-server -a 0.0.0.0 -p 9000"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux'
4 | import createStore from './store'
5 |
6 | const store = createStore()
7 |
8 | const Page = () => (
9 | Hello World
10 | )
11 |
12 | const ConnectedPage = firebaseConnect()(Page)
13 |
14 | const App = () => (
15 |
16 |
17 |
18 | );
19 |
20 | ReactDOM.render( , document.querySelector('#app'));
21 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/src/config.js:
--------------------------------------------------------------------------------
1 | export const firebase = {
2 | apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3 | authDomain: 'redux-firebasev3.firebaseapp.com',
4 | databaseURL: 'https://redux-firebasev3.firebaseio.com',
5 | storageBucket: 'redux-firebasev3.appspot.com',
6 | messagingSenderId: '823357791673'
7 | }
8 |
9 | export default { firebase }
10 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { firebaseStateReducer as firebase } from 'react-redux-firebase'
3 |
4 | const rootReducer = combineReducers({
5 | firebase
6 | })
7 |
8 | export default rootReducer
9 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose } from 'redux'
2 | import rootReducer from './reducer'
3 | import { firebase as fbConfig } from './config'
4 | import { reactReduxFirebase } from 'react-redux-firebase'
5 |
6 | export default function configureStore (initialState, history) {
7 | const createStoreWithMiddleware = compose(
8 | reactReduxFirebase(fbConfig,
9 | {
10 | userProfile: 'users',
11 | enableLogging: false
12 | }
13 | ),
14 | typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
15 | )(createStore)
16 | const store = createStoreWithMiddleware(rootReducer)
17 |
18 | if (module.hot) {
19 | // Enable Webpack hot module replacement for reducers
20 | module.hot.accept('./reducer', () => {
21 | const nextRootReducer = require('./reducer')
22 | store.replaceReducer(nextRootReducer)
23 | })
24 | }
25 |
26 | return store
27 | }
28 |
--------------------------------------------------------------------------------
/examples/snippets/webpack2/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: {
6 | 'app': path.resolve(__dirname, 'src/app')
7 | },
8 | output: {
9 | path: path.join(__dirname, 'dist'),
10 | filename: '[name].bundle.js'
11 | },
12 | module: {
13 | rules: [{
14 | test: /\.(js|jsx)$/,
15 | loader: 'babel-loader',
16 | query: {
17 | presets: [
18 | [
19 | "es2015",
20 | // {
21 | // "modules": false
22 | // }
23 | ],
24 | 'react'
25 | ],
26 | plugins: []
27 | },
28 | include: [
29 | path.resolve(__dirname, 'src')
30 | ]
31 | }]
32 | },
33 | plugins: [
34 | // new webpack.DefinePlugin({
35 | // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
36 | // }),
37 | ],
38 | resolve: {
39 | modules: [
40 | 'node_modules'
41 | ],
42 | extensions: ['.js', '.json']
43 | },
44 | devtool: false
45 | };
46 |
--------------------------------------------------------------------------------
/src/ReactReduxFirebaseContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | /**
4 | * @description Context for extended firebase instance created
5 | * by react-redux-firebase
6 | */
7 | const ReactReduxFirebaseContext = createContext(null)
8 |
9 | export default ReactReduxFirebaseContext
10 |
--------------------------------------------------------------------------------
/src/ReduxFirestoreContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | /**
4 | * @description Context for extended firebase instance created
5 | * by react-redux-firebase
6 | */
7 | const ReduxFirestoreContext = createContext(null)
8 |
9 | export default ReduxFirestoreContext
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import createFirebaseInstance, { getFirebase } from './createFirebaseInstance'
2 | import ReactReduxFirebaseProvider from './ReactReduxFirebaseProvider'
3 | import ReactReduxFirebaseContext from './ReactReduxFirebaseContext'
4 | import ReduxFirestoreProvider from './ReduxFirestoreProvider'
5 | import ReduxFirestoreContext from './ReduxFirestoreContext'
6 | import firebaseConnect from './firebaseConnect'
7 | import firestoreConnect from './firestoreConnect'
8 | import withFirebase from './withFirebase'
9 | import withFirestore from './withFirestore'
10 | import useFirebaseConnect from './useFirebaseConnect'
11 | import useFirestoreConnect from './useFirestoreConnect'
12 | import useFirebase from './useFirebase'
13 | import useFirestore from './useFirestore'
14 | import reducer from './reducer'
15 | import constants, { actionTypes } from './constants'
16 | import { authIsReady } from './utils/auth'
17 | import * as helpers from './helpers'
18 |
19 | export default {
20 | ReactReduxFirebaseProvider,
21 | ReactReduxFirebaseContext,
22 | ReduxFirestoreContext,
23 | ReduxFirestoreProvider,
24 | createFirebaseInstance,
25 | firebaseConnect,
26 | firestoreConnect,
27 | withFirebase,
28 | withFirestore,
29 | useFirebase,
30 | useFirebaseConnect,
31 | useFirestore,
32 | useFirestoreConnect,
33 | reducer,
34 | firebaseReducer: reducer,
35 | constants,
36 | actionTypes,
37 | getFirebase,
38 | authIsReady,
39 | ...helpers
40 | }
41 |
--------------------------------------------------------------------------------
/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from './utils/reducers'
2 | import {
3 | requestingReducer,
4 | requestedReducer,
5 | timestampsReducer,
6 | dataReducer,
7 | orderedReducer,
8 | authReducer,
9 | authErrorReducer,
10 | profileReducer,
11 | listenersReducer,
12 | isInitializingReducer,
13 | errorsReducer
14 | } from './reducers'
15 |
16 | /**
17 | * @name firebaseReducer
18 | * Main reducer for react-redux-firebase. This function is called
19 | * automatically by redux every time an action is fired. Based on which action
20 | * is called and its payload, the reducer will update redux state with relevant
21 | * changes. `firebaseReducer` is made up of multiple "slice reducers"
22 | * ([outlined in reducers docs](/docs/recipes/reducers.md)) combined using
23 | * [`combineReducers`](https://redux.js.org/docs/api/combineReducers.html)
24 | * following the patterns outlined in
25 | * [the redux docs](https://redux.js.org/docs/recipes/StructuringReducers.html).
26 | * @param {object} state - Current Firebase Redux State (state.firebase)
27 | * @param {object} action - Action which will modify state
28 | * @param {string} action.type - Type of Action being called
29 | * @param {string} action.path - Path of action that was dispatched
30 | * @param {string} action.data - Data associated with action
31 | * @returns {object} Firebase redux state
32 | */
33 | export default combineReducers({
34 | requesting: requestingReducer,
35 | requested: requestedReducer,
36 | timestamps: timestampsReducer,
37 | data: dataReducer,
38 | ordered: orderedReducer,
39 | auth: authReducer,
40 | authError: authErrorReducer,
41 | profile: profileReducer,
42 | listeners: listenersReducer,
43 | isInitializing: isInitializingReducer,
44 | errors: errorsReducer
45 | })
46 |
--------------------------------------------------------------------------------
/src/useFirebase.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import ReactReduxFirebaseContext from './ReactReduxFirebaseContext'
3 |
4 | /**
5 | * @name useFirebase
6 | * @description React hook that provides `firebase` object.
7 | * Firebase is gathered from ReactReduxFirebaseContext, which is
8 | * set by createFirebaseInstance during setup.
9 | * **NOTE**: This version of the Firebase library has extra methods, config,
10 | * and functionality which give it it's capabilities such as dispatching
11 | * actions.
12 | * @returns {object} - Extended Firebase instance
13 | * @see https://react-redux-firebase.com/docs/api/useFirebase.html
14 | * @example Basic
15 | * import { useFirebase } from 'react-redux-firebase'
16 | *
17 | * export default function AddData() {
18 | * const firebase = useFirebase()
19 | *
20 | * function addTodo() {
21 | * const exampleTodo = { done: false, text: 'Sample' }
22 | * return firebase.push('todos', exampleTodo)
23 | * }
24 | *
25 | * return (
26 | *
27 | *
28 | * Add Sample Todo
29 | *
30 | *
31 | * )
32 | * }
33 | */
34 | export default function useFirebase() {
35 | return useContext(ReactReduxFirebaseContext)
36 | }
37 |
--------------------------------------------------------------------------------
/src/useFirestore.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import ReduxFirestoreContext from './ReduxFirestoreContext'
3 |
4 | /**
5 | * @name useFirestore
6 | * @description React hook that return firestore object.
7 | * @returns {object} - Extended Firestore instance
8 | * @see https://react-redux-firebase.com/docs/api/useFirestore.html
9 | * @example Basic
10 | * import React from 'react'
11 | * import { useFirestore } from 'react-redux-firebase'
12 | *
13 | * export default function AddData() {
14 | * const firestore = useFirestore()
15 | *
16 | * function addTodo() {
17 | * const exampleTodo = { done: false, text: 'Sample' }
18 | * return firestore.collection('todos').add(exampleTodo)
19 | * }
20 | *
21 | * return (
22 | *
23 | *
24 | * Add Sample Todo
25 | *
26 | *
27 | * )
28 | * }
29 | */
30 | export default function useFirestore() {
31 | return useContext(ReduxFirestoreContext)
32 | }
33 |
--------------------------------------------------------------------------------
/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: '../.eslintrc.js',
3 |
4 | globals: {
5 | sinon: true,
6 | expect: true,
7 | after: true,
8 | afterEach: true,
9 | before: true,
10 | beforeEach: true,
11 | it: true,
12 | describe: true,
13 | Firebase: true,
14 | firebase: true,
15 | fbConfig: true,
16 | uid: true,
17 | existingProfile: true
18 | },
19 |
20 | rules: {
21 | 'no-unused-expressions': [0]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --require @babel/register
2 | --require ./test/setup
3 | --recursive
4 | --colors
5 | --report lcov
6 |
--------------------------------------------------------------------------------
/test/unit/library.spec.js:
--------------------------------------------------------------------------------
1 | import src from '../../src'
2 |
3 | describe('module', () => {
4 | describe('exports', () => {
5 | it('ReactReduxFirebaseContext', () => {
6 | expect(src).to.have.property('ReactReduxFirebaseContext')
7 | })
8 | it('ReactReduxFirebaseProvider', () => {
9 | expect(src).to.respondTo('ReactReduxFirebaseProvider')
10 | })
11 | it('ReduxFirestoreContext', () => {
12 | expect(src).to.have.property('ReduxFirestoreContext')
13 | })
14 | it('ReduxFirestoreProvider', () => {
15 | expect(src).to.respondTo('ReduxFirestoreProvider')
16 | })
17 | it('withFirestore', () => {
18 | expect(src).to.respondTo('withFirestore')
19 | })
20 | it('withFirebase', () => {
21 | expect(src).to.respondTo('withFirebase')
22 | })
23 | it('firebaseReducer', () => {
24 | expect(src).to.respondTo('firebaseReducer')
25 | })
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/test/unit/useFirebase.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TestUtils from 'react-dom/test-utils'
3 | import { firebaseWithConfig } from '../utils'
4 | import ReactReduxFirebaseProvider from '../../src/ReactReduxFirebaseProvider'
5 | import useFirebase from '../../src/useFirebase'
6 |
7 | describe('useFirebase', () => {
8 | it('return firebase object', () => {
9 | const spy = sinon.spy()
10 | const dispatchSpy = sinon.spy()
11 | const InnerComponent = ({ spy }) => {
12 | const firebase = useFirebase()
13 | spy(firebase)
14 | return null
15 | }
16 | TestUtils.renderIntoDocument(
17 |
21 |
22 |
23 | )
24 | expect(spy).to.has.been.called
25 | expect(spy.lastCall.args[0]).to.respondTo('push')
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/test/unit/useFirestore.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TestUtils from 'react-dom/test-utils'
3 | import { firebaseWithConfig } from '../utils'
4 | import ReactReduxFirebaseProvider from '../../src/ReactReduxFirebaseProvider'
5 | import useFirestore from '../../src/useFirestore'
6 | import { createFirestoreInstance } from 'redux-firestore'
7 |
8 | describe('useFirestore', () => {
9 | it('return firestore object', () => {
10 | const spy = sinon.spy()
11 | const dispatchSpy = sinon.spy()
12 | const InnerComponent = ({ spy }) => {
13 | const firestore = useFirestore()
14 | spy(firestore)
15 | return null
16 | }
17 | TestUtils.renderIntoDocument(
18 |
23 |
24 |
25 | )
26 | expect(spy).to.has.been.called
27 | expect(spy.lastCall.args[0]).to.respondTo('add')
28 | })
29 | })
30 |
--------------------------------------------------------------------------------
/test/unit/utils/actions.spec.js:
--------------------------------------------------------------------------------
1 | import { wrapInDispatch } from 'utils/actions'
2 | const method = () => Promise.resolve()
3 | const failMethod = () => Promise.reject(new Error('Some Error'))
4 | const dispatch = () => {}
5 |
6 | describe('Utils: Auth', () => {
7 | describe('wrapInDispatch', () => {
8 | // Skipped due to capatalize and auth provider function
9 | it('creates valid Auth Provider', () => {
10 | // TODO: Check that dispatch is called with actions
11 | expect(
12 | wrapInDispatch(dispatch, { method, args: ['arg1'], types: ['ACTION'] })
13 | ).to.be.fulfilled
14 | })
15 | it('handles string list of scopes', () => {
16 | expect(
17 | wrapInDispatch(dispatch, {
18 | method: failMethod,
19 | args: ['arg1'],
20 | types: ['ACTION']
21 | })
22 | ).to.be.rejectedWith('Failed')
23 | })
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/test/unit/utils/index.spec.js:
--------------------------------------------------------------------------------
1 | import { createCallable } from 'utils'
2 |
3 | describe('Utils: Index', () => {
4 | describe('createCallable', () => {
5 | it.skip('calls a passed function', () => {
6 | const spy = sinon.spy()
7 | createCallable(spy)
8 | expect(spy).to.have.been.called.once
9 | })
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/test/unit/utils/storage.spec.js:
--------------------------------------------------------------------------------
1 | import { deleteFile } from 'utils/storage'
2 | import { fakeFirebase } from '../../utils'
3 |
4 | describe('Utils: Storage', () => {
5 | describe('deleteFile', () => {
6 | it('returns dbPath', () =>
7 | expect(
8 | deleteFile(fakeFirebase, { path: 'some', dbPath: 'some' })
9 | ).to.eventually.have.keys(['path', 'dbPath']))
10 | it('returns dbPath', () =>
11 | expect(
12 | deleteFile(fakeFirebase, { path: 'some' })
13 | ).to.eventually.have.keys('path'))
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/test/unit/withFirebase.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createContainer, TestLeaf } from '../utils'
3 | import withFirebase from '../../src/withFirebase'
4 |
5 | let wrapper
6 | let leaf
7 |
8 | describe('withFirebase', () => {
9 | beforeEach(() => {
10 | const container = createContainer({ hoc: withFirebase })
11 | wrapper = container.wrapper
12 | leaf = container.leaf
13 | })
14 |
15 | it('adds firebase as prop', () => {
16 | expect(leaf.prop('firebase')).to.exist
17 | expect(leaf.prop('firebase')).to.respondTo('push')
18 | })
19 |
20 | it('adds dispatch as prop', () => {
21 | expect(leaf.prop('dispatch')).to.exist
22 | expect(leaf.prop('dispatch')).to.be.a.function
23 | })
24 |
25 | describe('sets displayName static as', () => {
26 | /* eslint-disable no-template-curly-in-string */
27 | describe('withFirebase(${WrappedComponentName}) for', () => {
28 | /* eslint-enable no-template-curly-in-string */
29 | it('standard components', () => {
30 | const component = withFirebase(TestLeaf)
31 | expect(component.displayName).to.equal(`withFirebase(TestLeaf)`)
32 | })
33 |
34 | it('string components', () => {
35 | const str = 'Test'
36 | const stringComp = withFirebase(str)
37 | expect(stringComp.displayName).to.equal(`withFirebase(${str})`)
38 | })
39 | })
40 |
41 | it('"Component" for all other types', () => {
42 | wrapper = withFirebase(() =>
)
43 | expect(wrapper.displayName).to.equal('withFirebase(Component)')
44 | })
45 | })
46 |
47 | it('sets WrappedComponent static as component which was wrapped', () => {
48 | expect(leaf).to.match(TestLeaf)
49 | })
50 | })
51 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const webpack = require('webpack')
4 | const pkg = require('./package.json')
5 | const env = process.env.NODE_ENV
6 | const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
7 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
8 | .BundleAnalyzerPlugin
9 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
10 |
11 | const config = {
12 | module: {
13 | rules: [
14 | { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }
15 | ]
16 | },
17 | output: {
18 | library: 'ReactReduxFirebase',
19 | libraryTarget: 'umd'
20 | },
21 | externals: {
22 | react: {
23 | commonjs: 'react',
24 | commonjs2: 'react',
25 | amd: 'react',
26 | root: 'React'
27 | },
28 | firebase: {
29 | commonjs: 'firebase',
30 | commonjs2: 'firebase',
31 | amd: 'firebase',
32 | root: 'Firebase'
33 | },
34 | 'prop-types': {
35 | commonjs: 'prop-types',
36 | commonjs2: 'prop-types',
37 | amd: 'prop-types',
38 | root: 'PropTypes'
39 | }
40 | },
41 | plugins: [new LodashModuleReplacementPlugin()]
42 | }
43 |
44 | if (process.env.SIZE) {
45 | config.plugins.push(new BundleAnalyzerPlugin())
46 | }
47 |
48 | if (env === 'production') {
49 | config.plugins.push(new UglifyJsPlugin())
50 | }
51 |
52 | config.plugins.push(
53 | new webpack.BannerPlugin({
54 | banner: `${pkg.name}${env === 'production' ? '.min' : ''}.js v${
55 | pkg.version
56 | }`,
57 | raw: false,
58 | entryOnly: true
59 | })
60 | )
61 |
62 | module.exports = config
63 |
--------------------------------------------------------------------------------