Repository: btomashvili/react-redux-firebase-boilerplate Branch: master Commit: fb06742c53a3 Files: 40 Total size: 41.4 KB Directory structure: gitextract_tc2n1gmc/ ├── .babelrc ├── .editorconfig ├── .eslintrc ├── .firebaserc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── database.rules.json ├── firebase.json ├── jsconfig.json ├── package.json ├── src/ │ ├── app/ │ │ ├── actions/ │ │ │ ├── firebase_actions.js │ │ │ ├── index.js │ │ │ └── types.js │ │ ├── bundle.scss │ │ ├── components/ │ │ │ ├── app.jsx │ │ │ ├── app.scss │ │ │ ├── helpers/ │ │ │ │ └── loading.jsx │ │ │ ├── index_home.jsx │ │ │ └── user/ │ │ │ ├── change_password.jsx │ │ │ ├── login.jsx │ │ │ ├── logout.jsx │ │ │ ├── profile.jsx │ │ │ ├── register.jsx │ │ │ └── reset_password.jsx │ │ ├── config.js │ │ ├── index.jsx │ │ ├── reducers/ │ │ │ ├── firebase_user_reducer.js │ │ │ └── index.js │ │ ├── routes.jsx │ │ └── utils/ │ │ ├── authenticated.js │ │ └── firebase.js │ └── index.html ├── test/ │ ├── components/ │ │ └── app_test.js │ └── test_helper.js ├── webpack/ │ ├── webpack-dev.config.js │ ├── webpack-prod.config.js │ └── webpack.config.js └── www.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["react", "es2015", "stage-1"] } ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true max_line_length = 80 trim_trailing_whitespace = true [*.md] max_line_length = 0 trim_trailing_whitespace = false [COMMIT_EDITMSG] max_line_length = 0 ================================================ FILE: .eslintrc ================================================ { "extends": "airbnb", "parserOptions": { "ecmaFeatures": { "experimentalObjectRestSpread": true } }, "rules": { "max-len": ["warn", 120], "indent": ["warn", 4], "react/jsx-indent": ["warn", 4] }, "globals": { "localStorage": true } } ================================================ FILE: .firebaserc ================================================ { "projects": { "default": "react-redux-firebase-d6283" } } ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* # dist # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history .idea/ .vscode firebase-debug.log ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "node" - "6" - "5" - "4" ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 btomashvili Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # THIS PROJECT IS DEPRECATED IT'S NOT MAINTAINED ANYMORE! # React, Redux Firebase Boilerplate [![Build Status](https://travis-ci.org/awwong1/react-redux-firebase-boilerplate.svg?branch=master)](https://travis-ci.org/awwong1/react-redux-firebase-boilerplate) > [Firebase](https://www.firebase.com) is a powerful platform for your mobile and web applications that lets you build apps fast without managing servers. Firebase gives you the tools and infrastructure to build better apps and grow successful businesses. > [React](https://www.firebase.com) A javascript library for building user interfaces > [Redux](http://redux.js.org/) Redux is a predictable state container for JavaScript apps. ### Boilerplate Introduction Boilerplate is designed for quickly spin up your apps with Firebase, using bunch of awesome new front-end technologies includes webpack build system, hot reloading, routing & sass support. ## Features * [react](https://github.com/facebook/react) * [redux](https://github.com/rackt/redux) * [firebase](https://www.npmjs.com/package/firebase) * [react-router](https://github.com/rackt/react-router) * [redux-promise](https://github.com/acdlite/redux-promise) * [webpack](https://github.com/webpack/webpack) * [babel](https://github.com/babel/babel) Quick Start ----------- ```shell $ git clone https://github.com/btomashvili/react-redux-firebase-boilerplate.git $ cd react-redux-firebase-boilerplate $ npm install $ npm run dev ``` Firebase settings -------- First you need to create your firebase application to fetch settings for boilerplate. For more information how to add your web app check this [resource](https://firebase.google.com/docs/web/setup). After it copy your settings from firebase and fill config.js ```javascript module.exports = { FIREBASE_CONFIG: { apiKey: "", authDomain: "", databaseURL: "", storageBucket: "", } } ``` Commands -------- |Script|Description| |---|---| |`npm run dev`| Run development server with webpack-dev-server @ `localhost:3000`| |`npm run build`| Test, and build the application to `./dist`| |`npm start`| Start production ready app with pm2 from `./dist` @ `localhost:8080`| |`npm run lint`| Run ESLint on `./src`| What it looks like ### DEMO [https://react-redux-firebase-d6283.firebaseapp.com/](https://react-redux-firebase-d6283.firebaseapp.com/) -------- ![screen](https://www.dropbox.com/s/csufxlitjme8p3q/react_redux_firebase.gif?raw=1 "react_redux_firebase_boilerplate") ================================================ FILE: database.rules.json ================================================ { "rules": { ".read": false, ".write": false, "words": { ".read": true, ".write": true }, "dev": { ".read": true, ".write": true }, "staging": { ".read": true, ".write": true } } } ================================================ FILE: firebase.json ================================================ { "database": { "rules": "database.rules.json" }, "hosting": { "public": "dist", "rewrites": [ { "source": "**", "destination": "/index.html" } ] } } ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "target": "ES6", "allowSyntheticDefaultImports": true }, "exclude": [ "node_modules" ] } ================================================ FILE: package.json ================================================ { "name": "react-redux-firebase-boilerplate", "version": "1.0.0", "description": "Simple boilerplate for Reactjs with Redux and Firebase", "main": "index.jsx", "repository": "git@github.com:btomashvili/react-redux-firebase-boilerplate.git", "scripts": { "dev": "./node_modules/.bin/webpack-dev-server --config ./webpack/webpack-dev.config.js --watch --colors", "build": "rm -rf dist && ./node_modules/.bin/webpack --config ./webpack/webpack-prod.config.js --colors", "start": "PORT=8080 pm2 start ./www.js", "test": "./node_modules/.bin/eslint src && ./node_modules/.bin/mocha --compilers js:babel-core/register --require ./test/test_helper.js --recursive ./test", "test:watch": "npm run test -- --watch", "lint": "./node_modules/.bin/eslint src" }, "author": "Beka Tomashvili", "contributors": [ { "name": "Alexander Wong", "email": "admin@alexander-wong.com", "url": "https://www.alexander-wong.com/" } ], "license": "MIT", "keywords": [ "ReactJS", "Redux", "Firebase", "React hot loader", "React Router", "ESLint" ], "devDependencies": { "babel-core": "^6.2.1", "babel-loader": "^6.2.0", "babel-preset-es2015": "^6.1.18", "babel-preset-react": "^6.1.18", "babel-preset-stage-1": "^6.5.0", "chai": "^3.5.0", "chai-jquery": "^2.0.0", "css-loader": "^0.23.1", "eslint": "^3.6.1", "eslint-config-airbnb": "^12.0.0", "eslint-plugin-import": "^1.16.0", "eslint-plugin-jsx-a11y": "^2.2.2", "eslint-plugin-react": "^6.3.0", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "html-webpack-plugin": "^2.21.0", "imports-loader": "^0.6.5", "jquery": "^3.1.0", "jsdom": "^8.1.0", "mocha": "^2.4.5", "node-sass": "^3.8.0", "react-addons-test-utils": "^0.14.8", "react-hot-loader": "^1.3.0", "sass-loader": "^4.0.0", "style-loader": "^0.13.1", "url-loader": "^0.5.7", "webpack": "^1.12.9", "webpack-dev-server": "^1.14.0" }, "dependencies": { "bootstrap-social": "^5.0.0", "eslint-config-airbnb": "^12.0.0", "eslint-plugin-import": "^2.0.0", "express": "^4.14.0", "firebase": "^3.0.5", "font-awesome": "^4.6.3", "lodash": "^3.10.1", "react": "^0.14.8", "react-dom": "^0.14.3", "react-redux": "^4.0.0", "react-router": "^2.0.0-rc5", "redux": "^3.0.4", "redux-logger": "^3.0.6", "redux-promise": "^0.5.3", "redux-thunk": "^2.1.0", "reduxsauce": "^0.5.0", "seamless-immutable": "^7.1.2" } } ================================================ FILE: src/app/actions/firebase_actions.js ================================================ import { LOGIN_WITH_PROVIDER_FIREBASE, REGISTER_FIREBASE_USER, LOGIN_FIREBASE_USER, FETCH_FIREBASE_USER, UPDATE_FIREBASE_USER, CHANGE_FIREBASE_USER_PASSWORD, FIREBASE_PASSWORD_RESET_EMAIL, LOGOUT_FIREBASE_USER, } from './types'; export function loginWithProvider(provider) { return { type: LOGIN_WITH_PROVIDER_FIREBASE, provider, }; } export function registerUser(user) { return { type: REGISTER_FIREBASE_USER, user }; } export function loginUser(user) { return { type: LOGIN_FIREBASE_USER, user }; } export function fetchUser() { return { type: FETCH_FIREBASE_USER }; } export function updateUser(user) { return { type: UPDATE_FIREBASE_USER, user }; } export function changePassword(newPassword) { return { type: CHANGE_FIREBASE_USER_PASSWORD, newPassword }; } export function resetPasswordEmail(email) { return { type: FIREBASE_PASSWORD_RESET_EMAIL, email }; } export function logoutUser(user) { return { type: LOGOUT_FIREBASE_USER, user }; } ================================================ FILE: src/app/actions/index.js ================================================ ================================================ FILE: src/app/actions/types.js ================================================ // / FIREBASE AUTH ACTIONS export const LOGIN_WITH_PROVIDER_FIREBASE = 'LOGIN_WITH_PROVIDER_FIREBASE'; export const REGISTER_FIREBASE_USER = 'REGISTER_FIREBASE_USER'; export const LOGIN_FIREBASE_USER = 'LOGIN_FIREBASE_USER'; export const FETCH_FIREBASE_USER = 'FETCH_FIREBASE_USER'; export const UPDATE_FIREBASE_USER = 'UPDATE_FIREBASE_USER'; export const CHANGE_FIREBASE_USER_PASSWORD = 'CHANGE_FIREBASE_USER_PASSWORD'; export const FIREBASE_PASSWORD_RESET_EMAIL = 'FIREBASE_PASSWORD_RESET_EMAIL'; export const LOGOUT_FIREBASE_USER = 'LOGOUT_FIREBASE_USER'; ================================================ FILE: src/app/bundle.scss ================================================ @import './components/app'; ================================================ FILE: src/app/components/app.jsx ================================================ import React, { Component } from 'react'; import { Link } from 'react-router'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { fetchUser, logoutUser } from '../actions/firebase_actions'; import FireBaseTools from '../utils/firebase' console.log(FireBaseTools.getDatabaseReference); class App extends Component { constructor(props) { super(props); this.props.fetchUser(); } _logOut() { this.props.logoutUser().then((data) => { // reload props from reducer this.props.fetchUser(); }); } _read() { FireBaseTools.getDatabaseReference('/DongerMaster').once('value').then(function(snapshot) { console.log(snapshot.val()); }); } _write() { FireBaseTools.getDatabaseReference('/').set({DongerMaster: 'lul'}); } _renderUserMenu(currentUser) { // if current user exists and user id exists than make user navigation if (currentUser && currentUser.uid) { return (
  • ); } else { return [
  • Login
  • ,
  • Register
  • , ]; } } render() { return (
    {this.props.children}
    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ fetchUser, logoutUser }, dispatch); } function mapStateToProps(state) { return { currentUser: state.currentUser }; } export default connect(mapStateToProps, mapDispatchToProps)(App); ================================================ FILE: src/app/components/app.scss ================================================ body { font-size: 16px; } ================================================ FILE: src/app/components/helpers/loading.jsx ================================================ import React from 'react'; const Loading = () => { return (
    Loading ....
    ); }; export default Loading; ================================================ FILE: src/app/components/index_home.jsx ================================================ import React from 'react'; export default () => { return
    Home Page of our application!
    ; }; ================================================ FILE: src/app/components/user/change_password.jsx ================================================ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { changePassword } from '../../actions/firebase_actions'; class ChangePassword extends Component { constructor(props) { super(props); this.onFormSubmit = this.onFormSubmit.bind(this); this.state = { message: '', }; } onFormSubmit(event) { event.preventDefault(); let password = this.refs.password.value; let repeatPassword = this.refs.repeatPassword.value; if (password !== repeatPassword) { this.setState({ message: 'Please password must match!', }); } else { this.props.changePassword(password).then((data) => { if (data.payload.errorCode) this.setState({ message: data.payload.errorMessage }); else this.setState({ message: 'Password was changed!' }); }); } } render() { return (

    Change Password

    {this.state.message}
    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ changePassword }, dispatch); } function mapStateToProps(state) { return { currentUser: state.currentUser }; } export default connect(mapStateToProps, mapDispatchToProps)(ChangePassword); ================================================ FILE: src/app/components/user/login.jsx ================================================ import React, { Component } from 'react'; import { browserHistory, Link } from 'react-router'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { loginUser, fetchUser, loginWithProvider } from '../../actions/firebase_actions'; class UserLogin extends Component { constructor(props) { super(props); this.onFormSubmit = this.onFormSubmit.bind(this); this.loginWithProvider = this.loginWithProvider.bind(this); this.state = { message: '', }; } onFormSubmit(event) { event.preventDefault(); const email = this.refs.email.value; const password = this.refs.password.value; this.props.loginUser({ email, password }).then((data) => { if (data.payload.errorCode) { this.setState({ message: data.payload.errorMessage }); } else { browserHistory.push('/profile'); } } ); } loginWithProvider(provider) { this.props.loginWithProvider(provider).then((data) => { if (data.payload.errorCode) { this.setState({ message: data.payload.errorMessage }); } else { browserHistory.push('/profile'); } }); } render() { return (

    {this.state.message}

    Login


    Forgot password?

    Login with

    { this.loginWithProvider('facebook'); }} data-provider="facebook" >Facebook { this.loginWithProvider('twitter'); }} data-provider="twitter" >Twitter { this.loginWithProvider('google'); }} data-provider="twitter" >Google { this.loginWithProvider('github'); }} data-provider="twitter" >Github
    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ loginUser, fetchUser, loginWithProvider, }, dispatch); } function mapStateToProps(state) { return { currentUser: state.currentUser }; } export default connect(mapStateToProps, mapDispatchToProps)(UserLogin); ================================================ FILE: src/app/components/user/logout.jsx ================================================ import React from 'react'; export default () => (

    You are logged out!

    ); ================================================ FILE: src/app/components/user/profile.jsx ================================================ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import firebase from '../../utils/firebase'; import { fetchUser, updateUser } from '../../actions/firebase_actions'; import Loading from '../helpers/loading'; import ChangePassword from './change_password'; class UserProfile extends Component { constructor(props) { super(props); this.props.fetchUser(); this.state = { message: '', }; this.onFormSubmit = this.onFormSubmit.bind(this); } onFormSubmit(event) { event.preventDefault(); const email = this.refs.email.value; const displayName = this.refs.displayName.value; this.props.updateUser({ email, displayName }).then((data) => { if (data.payload.errorCode) { this.setState({ message: data.payload.errorMessage }); } else { this.setState({ message: 'Updated successfuly!', }); } } ); } render() { if (!this.props.currentUser) { return ; } return (

    User Profile Page

    {this.state.message}


    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ fetchUser, updateUser }, dispatch); } function mapStateToProps(state) { return { currentUser: state.currentUser }; } export default connect(mapStateToProps, mapDispatchToProps)(UserProfile); ================================================ FILE: src/app/components/user/register.jsx ================================================ import React, { Component } from 'react'; import { browserHistory } from 'react-router'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { registerUser } from '../../actions/firebase_actions'; class UserRegister extends Component { constructor(props) { super(props); this.onFormSubmit = this.onFormSubmit.bind(this); this.state = { message: '', }; } onFormSubmit(event) { event.preventDefault(); const email = this.refs.email.value; const password = this.refs.password.value; this.props.registerUser({ email, password }).then((data) => { if (data.payload.errorCode) { this.setState({ message: data.payload.errorMessage }) ; } else { browserHistory.push('/profile'); } } ); } render() { return (

    {this.state.message}

    Register



    { this.loginWithProvider('facebook'); }} data-provider="facebook" >Facebook { this.loginWithProvider('twitter'); }} data-provider="twitter" >Twitter { this.loginWithProvider('google'); }} data-provider="twitter" >Google { this.loginWithProvider('github'); }} data-provider="twitter" >Github
    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ registerUser, }, dispatch); } function mapStateToProps(state) { return { currentUser: state.currentUser }; } export default connect(mapStateToProps, mapDispatchToProps)(UserRegister); ================================================ FILE: src/app/components/user/reset_password.jsx ================================================ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { resetPasswordEmail } from '../../actions/firebase_actions'; class ResetPassword extends Component { constructor(props) { super(props); this.state = { message: '', }; this.onFormSubmit = this.onFormSubmit.bind(this); } onFormSubmit(event) { event.preventDefault(); const email = this.refs.email.value; this.props.resetPasswordEmail(email).then((data) => { if (data.payload.errorCode) { this.setState({ message: data.payload.errorMessage }); } else { this.setState({ message: 'Please see your email!' }); } }); } render() { return (

    {this.state.message}

    ); } } function mapDispatchToProps(dispatch) { return bindActionCreators({ resetPasswordEmail, }, dispatch); } export default connect(null, mapDispatchToProps)(ResetPassword); ================================================ FILE: src/app/config.js ================================================ module.exports = { // Change this to your firebase configuration! (Add Firebase to your web app) FIREBASE_CONFIG: { apiKey: "AIzaSyBgD4q3YujiPOOt4sPAfBHzBtG6xENp-TE", authDomain: "adwebsite-928a9.firebaseapp.com", databaseURL: "https://adwebsite-928a9.firebaseio.com", projectId: "adwebsite-928a9", storageBucket: "adwebsite-928a9.appspot.com", messagingSenderId: "72060758788" }, }; ================================================ FILE: src/app/index.jsx ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware, compose } from 'redux'; import { Router, browserHistory } from 'react-router'; import { createLogger } from 'redux-logger'; import ReduxPromise from 'redux-promise'; import reducers from './reducers'; import routes from './routes'; import 'bootstrap-social'; // for bundling your styles import './bundle.scss'; const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore); // Redux Dev tools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const logger = createLogger(); const enhancers = [applyMiddleware(ReduxPromise, logger)]; const store = createStore(reducers, composeEnhancers(...enhancers)); ReactDOM.render( , document.querySelector('.react-root') ); ================================================ FILE: src/app/reducers/firebase_user_reducer.js ================================================ import FireBaseTools from '../utils/firebase'; import { LOGIN_WITH_PROVIDER_FIREBASE, REGISTER_FIREBASE_USER, LOGIN_FIREBASE_USER, FETCH_FIREBASE_USER, UPDATE_FIREBASE_USER, CHANGE_FIREBASE_USER_PASSWORD, FIREBASE_PASSWORD_RESET_EMAIL, LOGOUT_FIREBASE_USER, } from '../actions/types'; export default function (state = null, action) { switch (action.type) { case FETCH_FIREBASE_USER: return fetchUser(); case LOGOUT_FIREBASE_USER: return logoutUser(action.user); case REGISTER_FIREBASE_USER: return registerUser(action.user); case LOGIN_FIREBASE_USER: return loginUser(action.user); case UPDATE_FIREBASE_USER: return updateUser(action.user); case CHANGE_FIREBASE_USER_PASSWORD: return changePassword(action.newPassword); case FIREBASE_PASSWORD_RESET_EMAIL: return resetPasswordEmail(action.email); case LOGIN_WITH_PROVIDER_FIREBASE: return loginWithProvider(action.provider); default: return state; } } function loginWithProvider(provider) { FireBaseTools.loginWithProvider(provider); } function registerUser(user) { FireBaseTools.registerUser(user); } function loginUser(user) { FireBaseTools.loginUser(user); } function fetchUser() { FireBaseTools.fetchUser(); } function updateUser(user) { FireBaseTools.updateUserProfile(user); } function changePassword(newPassword) { FireBaseTools.changePassword(newPassword); } function resetPasswordEmail(email) { FireBaseTools.resetPasswordEmail(email); } function logoutUser(user) { FireBaseTools.logoutUser(user); } ================================================ FILE: src/app/reducers/index.js ================================================ import { combineReducers } from 'redux'; import FireBaseUserReducer from './firebase_user_reducer'; const rootReducer = combineReducers({ currentUser: FireBaseUserReducer, }); export default rootReducer; ================================================ FILE: src/app/routes.jsx ================================================ import React from 'react'; import { Route, IndexRoute } from 'react-router'; import App from './components/app'; import HomeIndex from './components/index_home'; import UserLogin from './components/user/login'; import UserLogout from './components/user/logout'; import UserRegister from './components/user/register'; import UserProfile from './components/user/profile'; import ResetPassword from './components/user/reset_password'; import requireAuth from './utils/authenticated'; export default ( ); ================================================ FILE: src/app/utils/authenticated.js ================================================ function requireAuth(nextState, replace) { const key = Object.keys(localStorage).find(e => e.match(/firebase:authUser/)); const data = JSON.parse(localStorage.getItem(key)); if (data == null) { replace({ pathname: '/login', state: { nextPathname: nextState.location.pathname, }, }); } } module.exports = requireAuth; ================================================ FILE: src/app/utils/firebase.js ================================================ import firebase from 'firebase'; import { FIREBASE_CONFIG } from '../config'; export const firebaseApp = firebase.initializeApp(FIREBASE_CONFIG); export const firebaseAuth = firebaseApp.auth(); export const firebaseDb = firebaseApp.database(); const FireBaseTools = { /** * Return an instance of a firebase auth provider based on the provider string. * * @param provider * @returns {firebase.auth.AuthProvider} */ getProvider: (provider) => { switch (provider) { case 'email': return new firebase.auth.EmailAuthProvider(); case 'facebook': return new firebase.auth.FacebookAuthProvider(); case 'github': return new firebase.auth.GithubAuthProvider(); case 'google': return new firebase.auth.GoogleAuthProvider(); case 'twitter': return new firebase.auth.TwitterAuthProvider(); default: throw new Error('Provider is not supported!!!'); } }, /** * Login with provider => p is provider "email", "facebook", "github", "google", or "twitter" * Uses Popup therefore provider must be an OAuth provider. EmailAuthProvider will throw an error * * @returns {any|!firebase.Thenable.<*>|firebase.Thenable} */ loginWithProvider: (p) => { const provider = FireBaseTools.getProvider(p); return firebaseAuth.signInWithPopup(provider).then(firebaseAuth.currentUser).catch(error => ({ errorCode: error.code, errorMessage: error.message, })); }, /** * Register a user with email and password * * @param user * @returns {any|!firebase.Thenable.<*>|firebase.Thenable} */ registerUser: user => firebaseAuth.createUserWithEmailAndPassword(user.email, user.password) .then(userInfo => userInfo) .catch(error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Sign the user out * * @returns {!firebase.Promise.<*>|firebase.Thenable|firebase.Promise|!firebase.Thenable.<*>} */ logoutUser: () => firebaseAuth.signOut().then(() => ({ success: 1, message: 'logout', })), /** * Retrieve the current user (Promise) * @returns {Promise} */ fetchUser: () => new Promise((resolve, reject) => { const unsub = firebaseAuth.onAuthStateChanged((user) => { unsub(); resolve(user); }, (error) => { reject(error); }); }), /** * Log the user in using email and password * * @param user * @returns {any|!firebase.Thenable.<*>|firebase.Thenable} */ loginUser: user => firebaseAuth.signInWithEmailAndPassword(user.email, user.password) .then(userInfo => userInfo) .catch(error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Update a user's profile data * * @param u * @returns {!firebase.Promise.<*>|firebase.Thenable|firebase.Promise|!firebase.Thenable.<*>} */ updateUserProfile: u => firebaseAuth.currentUser.updateProfile(u).then(() => firebaseAuth.currentUser, error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Reset the password given the specified email * * @param email {string} * @returns {!firebase.Promise.<*>|firebase.Thenable|firebase.Promise|!firebase.Thenable.<*>} */ resetPasswordEmail: email => firebaseAuth.sendPasswordResetEmail(email).then(() => ({ message: 'Email sent', }), error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Update the user's password with the given password * * @param newPassword {string} * @returns {!firebase.Promise.<*>|firebase.Thenable|firebase.Promise|!firebase.Thenable.<*>} */ changePassword: newPassword => firebaseAuth.currentUser.updatePassword(newPassword).then(user => user, error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Send an account email verification message for the currently logged in user * * @returns {!firebase.Promise.<*>|firebase.Thenable|firebase.Promise|!firebase.Thenable.<*>} */ sendEmailVerification: () => firebaseAuth.currentUser.sendEmailVerification().then(() => ({ message: 'Email sent', }), error => ({ errorCode: error.code, errorMessage: error.message, })), /** * Get the firebase database reference. * * @param path {!string|string} * @returns {!firebase.database.Reference|firebase.database.Reference} */ getDatabaseReference: path => firebaseDb.ref(path), }; export default FireBaseTools; ================================================ FILE: src/index.html ================================================ React, Redux & Firebase Boilerplate
    ================================================ FILE: test/components/app_test.js ================================================ import {renderComponent, expect} from '../test_helper'; import App from '../../src/app/components/app'; describe('App', () => { let component; beforeEach(() => { component = renderComponent(App); }); it('renders something', () => { expect(component).to.exist; }); }); ================================================ FILE: test/test_helper.js ================================================ import _$ from 'jquery'; import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; import jsdom from 'jsdom'; import chai, { expect } from 'chai'; import chaiJquery from 'chai-jquery'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import reducers from '../src/app/reducers'; global.document = jsdom.jsdom(''); global.window = global.document.defaultView; global.navigator = global.window.navigator; const $ = _$(window); chaiJquery(chai, chai.util, $); function renderComponent(ComponentClass, props = {}, state = {}) { const componentInstance = TestUtils.renderIntoDocument( ); return $(ReactDOM.findDOMNode(componentInstance)); } $.fn.simulate = function(eventName, value) { if (value) { this.val(value); } TestUtils.Simulate[eventName](this[0]); }; export {renderComponent, expect}; ================================================ FILE: webpack/webpack-dev.config.js ================================================ module.exports = require('./webpack.config.js')({ isProduction: false, devtool: 'cheap-module-source-map', jsFileName: 'app.js', cssFileName: 'app.css', port: 3000, }); ================================================ FILE: webpack/webpack-prod.config.js ================================================ module.exports = require('./webpack.config.js')({ isProduction: true, devtool: 'source-map', jsFileName: 'app.[hash].js', cssFileName: 'app.[hash].css', }); ================================================ FILE: webpack/webpack.config.js ================================================ const Path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const Webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = (options) => { const ExtractSASS = new ExtractTextPlugin(`/styles/${options.cssFileName}`); const webpackConfig = { devtool: options.devtool, entry: [ `webpack-dev-server/client?http://localhost:${+options.port}`, 'webpack/hot/dev-server', Path.join(__dirname, '../src/app/index'), ], output: { path: Path.join(__dirname, '../dist'), filename: `/scripts/${options.jsFileName}`, }, resolve: { extensions: ['', '.js', '.jsx'], }, module: { loaders: [ {test: /.jsx?$/, include: Path.join(__dirname, '../src/app'), loader: 'babel',}, {test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel'}, {test: /\.css$/, loader: 'style-loader!css-loader'}, {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file"}, {test: /\.(woff|woff2)$/, loader: "url?prefix=font/&limit=5000"}, {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream"}, {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml"} ], }, plugins: [ new Webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(options.isProduction ? 'production' : 'development'), }, }), new HtmlWebpackPlugin({ template: Path.join(__dirname, '../src/index.html'), }), ], }; if (options.isProduction) { webpackConfig.entry = [Path.join(__dirname, '../src/app/index')]; webpackConfig.plugins.push( new Webpack.optimize.OccurenceOrderPlugin(), new Webpack.optimize.UglifyJsPlugin({ compressor: { warnings: false, }, }), ExtractSASS ); webpackConfig.module.loaders.push({ test: /\.scss$/, loader: ExtractSASS.extract(['css', 'sass']), }); } else { webpackConfig.plugins.push( new Webpack.HotModuleReplacementPlugin() ); webpackConfig.module.loaders.push({ test: /\.scss$/, loaders: ['style', 'css', 'sass'], }); webpackConfig.devServer = { contentBase: Path.join(__dirname, '../'), hot: true, port: options.port, inline: true, progress: true, historyApiFallback: true, }; } return webpackConfig; }; ================================================ FILE: www.js ================================================ const express = require('express'); const app = express(); app.use(express.static('./')); app.use(express.static('dist')); app.get('*', (req, res) => { res.sendFile(`${__dirname}/dist/index.html`); }); const port = process.env.PORT || 3000; app.listen(port, () => { console.log('app listening on', port); });