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
[](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/)
--------

================================================
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 (
{currentUser.email}
- Profile
- this._logOut}>Logout
);
} 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 (
);
}
}
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 (
);
}
}
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 () => (
);
================================================
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 (
);
}
}
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 (
);
}
}
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 (
);
}
}
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);
});