Add OAuth Login facebook,google,github

This commit is contained in:
Qolzam
2017-11-18 19:04:31 +07:00
parent f64b7bb24d
commit 92379b615c
23 changed files with 513 additions and 264 deletions

11
.babelrc Normal file
View File

@@ -0,0 +1,11 @@
{
"plugins": [
"react-hot-loader/babel",
"transform-decorators-legacy"
],
"presets": [
"babel-polyfill", ["env", { "modules": false }],
"react",
"stage-0"
]
}

View File

@@ -13,6 +13,7 @@
"author": "Amir Movahedi", "author": "Amir Movahedi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/react-hot-loader": "^3.0.5",
"amazon-cognito-identity-js": "^1.21.0", "amazon-cognito-identity-js": "^1.21.0",
"aws-sdk": "^2.132.0", "aws-sdk": "^2.132.0",
"axios": "^0.16.1", "axios": "^0.16.1",
@@ -39,6 +40,7 @@
"react-avatar-editor": "^10.3.0", "react-avatar-editor": "^10.3.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-event-listener": "^0.5.1", "react-event-listener": "^0.5.1",
"react-hot-loader": "^3.1.3",
"react-linkify": "^0.2.1", "react-linkify": "^0.2.1",
"react-parallax": "^1.4.4", "react-parallax": "^1.4.4",
"react-redux": "^5.0.6", "react-redux": "^5.0.6",
@@ -97,7 +99,8 @@
"tslint": "^5.7.0", "tslint": "^5.7.0",
"tslint-config-standard": "^6.0.1", "tslint-config-standard": "^6.0.1",
"typescript": "^2.5.3", "typescript": "^2.5.3",
"webpack": "^3.6.0" "webpack": "^3.6.0",
"webpack-hot-middleware": "^2.20.0"
}, },
"engines": { "engines": {
"node": "7.3.0", "node": "7.3.0",

View File

@@ -1,6 +1,9 @@
var express = require('express'); var express = require('express');
var morgan = require('morgan'); var morgan = require('morgan');
var path = require('path'); var path = require('path');
var webpack = require('webpack');
var webpackConfig = require('./webpack.config');
var compiler = webpack(webpackConfig);
// Create our app // Create our app
var app = express(); var app = express();
@@ -10,7 +13,14 @@ const PORT = process.env.PORT || 3000;
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms')); app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));
app.use(function (req, res, next){ app.use(require("webpack-dev-middleware")(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath
}));
app.use(require("webpack-hot-middleware")(compiler));
app.use(function(req, res, next) {
if (req.headers['x-forwarded-proto'] === 'https') { if (req.headers['x-forwarded-proto'] === 'https') {
res.redirect('http://' + req.hostname + req.url); res.redirect('http://' + req.hostname + req.url);
} else { } else {
@@ -34,6 +44,6 @@ app.get('*', (req, res) => {
}); });
app.listen(PORT, function () { app.listen(PORT, function() {
console.log('Express server is up on port ' + PORT); console.log('Express server is up on port ' + PORT);
}); });

View File

@@ -6,6 +6,7 @@ import { push } from 'react-router-redux'
// -Import domain // -Import domain
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { OAuthType, LoginUser } from 'core/domain/authorize'
import { UserRegisterModel } from 'models/users/userRegisterModel' import { UserRegisterModel } from 'models/users/userRegisterModel'
@@ -150,6 +151,27 @@ export const dbSendEmailVerfication = () => {
} }
} }
/**
* Login user with OAuth
*/
export const dbLoginWithOAuth = (type: OAuthType) => {
return (dispatch: any, getState: any) => {
dispatch(globalActions.showNotificationRequest())
return authorizeService.loginWithOAuth(type).then((result: LoginUser) => {
// Send email verification successful.
dispatch(globalActions.showNotificationSuccess())
dispatch(login(result.uid, true))
dispatch(push('/'))
})
.catch((error: SocialError) => {
// An error happened.
dispatch(globalActions.showErrorMessage(error.code))
})
}
}
/* _____________ CRUD State _____________ */ /* _____________ CRUD State _____________ */
/** /**

View File

@@ -27,9 +27,13 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
message: { message: {
fontWeight: 100 fontWeight: 100
}, },
sendButton: { buttons: {
marginTop: 60 marginTop: 60
},
homeButton: {
marginRight: 10
} }
} }
/** /**
@@ -89,9 +93,12 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
<p style={this.styles.message as any}> <p style={this.styles.message as any}>
An verificiation email has been already sent to you. Please check your inbox. If you couldn't see the emai, please resend email verification. An verificiation email has been already sent to you. Please check your inbox. If you couldn't see the emai, please resend email verification.
</p> </p>
<div style={this.styles.sendButton}> <div style={this.styles.buttons}>
<RaisedButton style={this.styles.homeButton} label='Home' primary={true} onClick={() => this.props.homePage()} />
<RaisedButton label='Send Email Verification' primary={true} onClick={() => this.props.sendEmailVerification()} /> <RaisedButton label='Send Email Verification' primary={true} onClick={() => this.props.sendEmailVerification()} />
</div> </div>
<div>
</div>
</div> </div>
</Paper> </Paper>
@@ -109,8 +116,8 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
*/ */
const mapDispatchToProps = (dispatch: Function, ownProps: IEmailVerificationComponentProps) => { const mapDispatchToProps = (dispatch: Function, ownProps: IEmailVerificationComponentProps) => {
return { return {
loginPage: () => { homePage: () => {
dispatch(push('/login')) dispatch(push('/'))
}, },
sendEmailVerification: () => dispatch(authorizeActions.dbSendEmailVerfication()) sendEmailVerification: () => dispatch(authorizeActions.dbSendEmailVerfication())
} }

View File

@@ -1,4 +1,5 @@
export interface IEmailVerificationComponentProps { export interface IEmailVerificationComponentProps {
sendEmailVerification: () => any sendEmailVerification: () => any
homePage: () => any
} }

View File

@@ -111,7 +111,8 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
goTo!('/login') goTo!('/login')
return return
} }
if (!isVerifide) { // if (!isVerifide) {
if (false) {
goTo!('/emailVerification') goTo!('/emailVerification')
} else if (!global.defaultLoadDataStatus) { } else if (!global.defaultLoadDataStatus) {

View File

@@ -1,3 +1,4 @@
import { OAuthType } from 'core/domain/authorize'
export interface ILoginComponentProps { export interface ILoginComponentProps {
/** /**
@@ -7,6 +8,13 @@ export interface ILoginComponentProps {
*/ */
login?: (email: string , password: string) => any login?: (email: string , password: string) => any
/**
* Login user with OAuth
*
* @memberof ILoginComponentProps
*/
loginWithOAuth: (type: OAuthType) => any
/** /**
* Redirect to signup page * Redirect to signup page
* *

View File

@@ -15,6 +15,8 @@ import ActionAndroid from 'material-ui/svg-icons/action/android'
import * as authorizeActions from 'actions/authorizeActions' import * as authorizeActions from 'actions/authorizeActions'
import { ILoginComponentProps } from './ILoginComponentProps' import { ILoginComponentProps } from './ILoginComponentProps'
import { ILoginComponentState } from './ILoginComponentState' import { ILoginComponentState } from './ILoginComponentState'
import { firebaseAuth } from 'data/firebaseClient'
import { OAuthType } from 'core/domain/authorize'
// - Create Login component class // - Create Login component class
export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> { export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> {
@@ -130,6 +132,8 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
display: 'block', display: 'block',
margin: 'auto' margin: 'auto'
} }
const {loginWithOAuth} = this.props
return ( return (
<form> <form>
@@ -163,12 +167,16 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
<div style={this.styles.singinOptions}> <div style={this.styles.singinOptions}>
<FlatButton <FlatButton
icon={<div className='icon-fb icon'></div>} icon={<div className='icon-fb icon'></div>}
onClick={() => loginWithOAuth(OAuthType.FACEBOOK)}
/> />
<FlatButton <FlatButton
icon={<div className='icon-google icon'></div>} icon={<div className='icon-google icon'></div>}
onClick={() => loginWithOAuth(OAuthType.GOOGLE)}
/> />
<FlatButton <FlatButton
icon={<div className='icon-github icon'></div>} icon={<div className='icon-github icon'></div>}
onClick={() => loginWithOAuth(OAuthType.GITHUB)}
/> />
</div> </div>
@@ -221,6 +229,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: ILoginComponentProps) => {
login: (email: string, password: string) => { login: (email: string, password: string) => {
dispatch(authorizeActions.dbLogin(email, password)) dispatch(authorizeActions.dbLogin(email, password))
}, },
loginWithOAuth: (type: OAuthType) => dispatch(authorizeActions.dbLoginWithOAuth(type)),
signupPage: () => { signupPage: () => {
dispatch(push('/signup')) dispatch(push('/signup'))
} }

View File

@@ -77,7 +77,6 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
componentDidCatch (error: any, info: any) { componentDidCatch (error: any, info: any) {
console.log('===========Catched by React componentDidCatch==============') console.log('===========Catched by React componentDidCatch==============')
console.log(error, info) console.log(error, info)
alert({error, info})
console.log('====================================') console.log('====================================')
} }

View File

@@ -1,7 +1,9 @@
import { LoginUser } from './loginResult' import { OAuthType } from './oauthType'
import { LoginUser } from './loginUser'
import { RegisterUserResult } from './registerUserResult' import { RegisterUserResult } from './registerUserResult'
export { export {
LoginUser, LoginUser,
RegisterUserResult RegisterUserResult,
OAuthType
} }

View File

@@ -1,23 +0,0 @@
import { BaseDomain } from 'core/domain/common'
export class LoginUser extends BaseDomain {
constructor (private _uid: string, private _emailVerified: boolean) {
super()
}
/**
* User identifier
*
* @type {string}
* @memberof LoginUser
*/
public get uid (): string {
return this._uid
}
public get emailVerified (): boolean {
return this._emailVerified
}
}

View File

@@ -0,0 +1,54 @@
import { BaseDomain } from 'core/domain/common'
export class LoginUser extends BaseDomain {
constructor (
private _uid: string,
private _emailVerified: boolean,
private _providerId: string = '',
private _displayName: string = '',
private _email: string = '',
private _avatarURL: string = ''
) {
super()
}
/**
* User identifier
*
* @type {string}
* @memberof LoginUser
*/
public get uid (): string {
return this._uid
}
/**
* If user's email is verifide {true} or not {false}
*
* @readonly
* @type {boolean}
* @memberof LoginUser
*/
public get emailVerified (): boolean {
return this._emailVerified
}
public get providerId (): string {
return this._providerId
}
public get displayName (): string {
return this._displayName
}
public get email (): string {
return this.email
}
public get avatarURL (): string {
return this._avatarURL
}
}

View File

@@ -0,0 +1,5 @@
export enum OAuthType {
GITHUB = 'GITHUB',
FACEBOOK = 'FACEBOOK',
GOOGLE = 'GOOGLE'
}

View File

@@ -1,8 +1,9 @@
import { BaseDomain } from 'core/domain/common' import { BaseDomain } from 'core/domain/common'
export class RegisterUserResult extends BaseDomain{ export class RegisterUserResult extends BaseDomain {
constructor(uid: string){ private _uid: string
constructor (uid: string) {
super() super()
this._uid = uid this._uid = uid
@@ -14,10 +15,7 @@ export class RegisterUserResult extends BaseDomain{
* @memberof LoginUser * @memberof LoginUser
*/ */
private _uid : string
public get uid (): string { public get uid (): string {
return this._uid return this._uid
} }
} }

View File

@@ -1,7 +1,9 @@
import {User} from './user' import { User } from './user'
import {Profile} from './profile' import { Profile } from './profile'
import { UserProvider } from './userProvider'
export { export {
User, User,
Profile Profile,
UserProvider
} }

View File

@@ -1,45 +1,14 @@
import { BaseDomain } from 'core/domain/common' import { BaseDomain } from 'core/domain/common'
export class Profile extends BaseDomain { export class Profile extends BaseDomain {
constructor (
public avatar: string,
public fullName: string,
public banner: string,
public tagLine: string,
public email?: string | null) {
super()
/** }
* User avatar address
*
* @type {string}
* @memberof Profile
*/
public avatar: string
/**
* User email
*
* @type {string}
* @memberof Profile
*/
public email?: string | null
/**
* User full name
*
* @type {string}
* @memberof Profile
*/
public fullName: string
/**
* The banner address of user profile
*
* @type {string}
* @memberof Profile
*/
public banner: string
/**
* User tag line
*
* @type {string}
* @memberof Profile
*/
public tagLine: string
} }

View File

@@ -0,0 +1,18 @@
/**
* User provide data
*
* @export
* @class UserProvider
*/
export class UserProvider {
constructor (
public userId: string,
public email: string,
public fullName: string,
public avatar: string,
public providerId: string,
public provider: string,
public accessToken: string
) {}
}

View File

@@ -1,5 +1,5 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { LoginUser, RegisterUserResult } from 'core/domain/authorize' import { LoginUser, RegisterUserResult, OAuthType } from 'core/domain/authorize'
/** /**
* Authentication service interface * Authentication service interface
@@ -55,4 +55,11 @@ export interface IAuthorizeService {
* @memberof IAuthorizeService * @memberof IAuthorizeService
*/ */
sendEmailVerification: () => Promise<void> sendEmailVerification: () => Promise<void>
/**
* Login user by OAuth authentication
*
* @memberof IAuthorizeService
*/
loginWithOAuth: (type: OAuthType) => Promise<LoginUser>
} }

View File

@@ -1,11 +1,13 @@
// - Import react components // - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient' import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import { IAuthorizeService } from 'core/services/authorize' import { IAuthorizeService } from 'core/services/authorize'
import { User } from 'core/domain/users' import { User, UserProvider } from 'core/domain/users'
import { LoginUser, RegisterUserResult } from 'core/domain/authorize' import { LoginUser, RegisterUserResult } from 'core/domain/authorize'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { OAuthType } from 'core/domain/authorize/oauthType'
/** /**
* Firbase authorize service * Firbase authorize service
* *
@@ -66,15 +68,8 @@ export class AuthorizeService implements IAuthorizeService {
firebaseAuth() firebaseAuth()
.createUserWithEmailAndPassword(user.email as string, user.password as string) .createUserWithEmailAndPassword(user.email as string, user.password as string)
.then((signupResult) => { .then((signupResult) => {
firebaseRef.child(`users/${signupResult.uid}/info`) const {uid, email, displayName, photoURL} = signupResult
.set({ this.storeUserInformation(uid,email,displayName,photoURL).then(resolve)
...user,
avatar: 'noImage'
})
.then((result) => {
resolve(new RegisterUserResult(signupResult.uid))
})
.catch((error: any) => reject(new SocialError(error.name, error.message)))
}) })
.catch((error: any) => reject(new SocialError(error.code, error.message))) .catch((error: any) => reject(new SocialError(error.code, error.message)))
}) })
@@ -140,6 +135,11 @@ export class AuthorizeService implements IAuthorizeService {
}) })
} }
/**
* Send verfication email to user email
*
* @memberof AuthorizeService
*/
public sendEmailVerification: () => Promise<void> = () => { public sendEmailVerification: () => Promise<void> = () => {
return new Promise<void>((resolve,reject) => { return new Promise<void>((resolve,reject) => {
let auth = firebaseAuth() let auth = firebaseAuth()
@@ -153,9 +153,109 @@ export class AuthorizeService implements IAuthorizeService {
reject(new SocialError(error.code, error.message)) reject(new SocialError(error.code, error.message))
}) })
} else { } else {
reject(new SocialError('nullException', 'User was null')); reject(new SocialError('authorizeService/nullException', 'User was null!'))
} }
}) })
} }
public loginWithOAuth: (type: OAuthType) => Promise<LoginUser> = (type) => {
return new Promise<LoginUser>((resolve,reject) => {
let provider: any
switch (type) {
case OAuthType.GITHUB:
provider = new firebaseAuth.GithubAuthProvider()
break
case OAuthType.FACEBOOK:
provider = new firebaseAuth.FacebookAuthProvider()
break
case OAuthType.GOOGLE:
provider = new firebaseAuth.GoogleAuthProvider()
break
default:
throw new SocialError('authorizeService/loginWithOAuth','None of OAuth type is matched!')
}
firebaseAuth().signInWithPopup(provider).then((result) => {
// This gives you a GitHub Access Token. You can use it to access the GitHub API.
let token = result.credential.accessToken
// The signed-in user info.
const {user} = result
const {credential} = result
const {uid, displayName, email, photoURL} = user
const {accessToken, provider, providerId} = credential
this.storeUserProviderData(uid,email,displayName,photoURL,providerId,provider,accessToken)
// this.storeUserInformation(uid,email,displayName,photoURL).then(resolve)
resolve(new LoginUser(user.uid,true,providerId,displayName,email,photoURL))
}).catch(function (error: any) {
// Handle Errors here.
let errorCode = error.code
let errorMessage = error.message
// The email of the user's account used.
let email = error.email
// The firebase.auth.AuthCredential type that was used.
let credential = error.credential
})
})
}
/**
* Store user information
*
* @private
* @memberof AuthorizeService
*/
private storeUserInformation = (userId: string, email: string, fullName: string, avatar?: string) => {
return new Promise<RegisterUserResult>((resolve,reject) => {
firebaseRef.child(`users/${userId}/info`)
.set({
userId,
avatar,
email,
fullName
})
.then((result) => {
resolve(new RegisterUserResult(userId))
})
.catch((error: any) => reject(new SocialError(error.name, error.message)))
})
}
/**
* Store user provider information
*
* @private
* @memberof AuthorizeService
*/
private storeUserProviderData = (
userId: string,
email: string,
fullName: string,
avatar: string,
providerId: string,
provider: string,
accessToken: string
) => {
return new Promise<RegisterUserResult>((resolve,reject) => {
firebaseRef.child(`users/${userId}/providerInfo`)
.set(new UserProvider(
userId,
email,
fullName,
avatar,
providerId,
provider,
accessToken
))
.then((result) => {
resolve(new RegisterUserResult(userId))
})
.catch((error: any) => reject(new SocialError(error.name, error.message)))
})
}
} }

View File

@@ -2,7 +2,7 @@
import { firebaseRef, firebaseAuth } from 'data/firebaseClient' import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { Profile } from 'core/domain/users' import { Profile, UserProvider } from 'core/domain/users'
import { IUserService } from 'core/services/users' import { IUserService } from 'core/services/users'
/** /**
@@ -15,22 +15,32 @@ import { IUserService } from 'core/services/users'
export class UserService implements IUserService { export class UserService implements IUserService {
public getUserProfile: (userId: string) public getUserProfile: (userId: string)
=> Promise<Profile> = (userId) => { => Promise<Profile> = (userId) => {
return new Promise<Profile>((resolve,reject) => { return new Promise<Profile>((resolve, reject) => {
let userProfileRef: any = firebaseRef.child(`users/${userId}/info`) let userProfileRef: any = firebaseRef.child(`users/${userId}/info`)
userProfileRef.once('value').then((snapshot: any) => { userProfileRef.once('value').then((snapshot: any) => {
let userProfile: Profile = snapshot.val() || {} let userProfile: Profile = snapshot.val() || {}
if (Object.keys(userProfile).length === 0 && userProfile.constructor === Object) {
this.getUserProviderData(userId).then((providerData: UserProvider) => {
const {avatar,fullName, email} = providerData
const userProfile = new Profile(avatar,fullName,'','',email)
resolve(userProfile) resolve(userProfile)
this.updateUserProfile(userId,userProfile)
})
} else {
resolve(userProfile)
}
}) })
.catch((error: any) => { .catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code, error.message))
}) })
}) })
} }
public updateUserProfile: (userId: string, profile: Profile) public updateUserProfile: (userId: string, profile: Profile)
=> Promise<void> = (userId, profile) => { => Promise<void> = (userId, profile) => {
return new Promise<void>((resolve,reject) => { return new Promise<void>((resolve, reject) => {
let updates: any = {} let updates: any = {}
updates[`users/${userId}/info`] = profile updates[`users/${userId}/info`] = profile
@@ -38,18 +48,18 @@ export class UserService implements IUserService {
resolve() resolve()
}) })
.catch((error: any) => { .catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code, error.message))
}) })
}) })
} }
public getUsersProfile: (userId: string) public getUsersProfile: (userId: string)
=> Promise<{ [userId: string]: Profile }> = (userId) => { => Promise<{ [userId: string]: Profile }> = (userId) => {
return new Promise<{ [userId: string]: Profile }>((resolve,reject) => { return new Promise<{ [userId: string]: Profile }>((resolve, reject) => {
let usersProfileRef: any = firebaseRef.child(`users`) let usersProfileRef: any = firebaseRef.child(`users`)
usersProfileRef.once('value').then((snapshot: any) => { usersProfileRef.once('value').then((snapshot: any) => {
let usersProfile: any = snapshot.val() || {} let usersProfile: any = snapshot.val() || {}
let parsedusersProfile: {[userId: string]: Profile} = {} let parsedusersProfile: { [userId: string]: Profile } = {}
Object.keys(usersProfile).forEach((userKey) => { Object.keys(usersProfile).forEach((userKey) => {
if (userId !== userKey) { if (userId !== userKey) {
let userInfo = usersProfile[userKey].info let userInfo = usersProfile[userKey].info
@@ -65,9 +75,27 @@ export class UserService implements IUserService {
resolve(parsedusersProfile) resolve(parsedusersProfile)
}) })
.catch((error: any) => { .catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code, error.message))
}) })
}) })
} }
private getUserProviderData = (userId: string) => {
return new Promise<UserProvider>((resolve,reject) => {
let userProviderRef: any = firebaseRef.child(`users/${userId}/providerInfo`)
userProviderRef.once('value').then((snapshot: any) => {
let userProviderRef: any = firebaseRef.child(`users/${userId}/info`)
let userProvider: UserProvider = snapshot.val() || {}
console.log('----------------userProfile')
console.log(userProvider)
console.log('-----------------------')
resolve(userProvider)
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
} }

View File

@@ -1,6 +1,7 @@
// Import external components refrence // Import external components refrence
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import injectTapEventPlugin from 'react-tap-event-plugin' import injectTapEventPlugin from 'react-tap-event-plugin'
import { cyan500 } from 'material-ui/styles/colors' import { cyan500 } from 'material-ui/styles/colors'
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme' import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'
@@ -20,7 +21,7 @@ import Master from 'components/master'
// Set default data // Set default data
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
store.subscribe(() => {}) store.subscribe(() => { })
// Needed for onTouchTap // Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941 // http://stackoverflow.com/a/34015469/988941
@@ -37,14 +38,35 @@ const muiTheme = getMuiTheme({
import 'applicationStyles' import 'applicationStyles'
const supportsHistory = 'pushState' in window.history const supportsHistory = 'pushState' in window.history
ReactDOM.render( // ReactDOM.render(
// <Provider store={store}>
// <ConnectedRouter history={history}>
// <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
// <Master />
// </MuiThemeProvider>
// </ConnectedRouter>
// </Provider>,
// document.getElementById('app')
// )
const render = (Component: any) => {
ReactDOM.render(
<AppContainer warnings={false}>
<Provider store={store}> <Provider store={store}>
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}> <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
<Master /> <Component />
{/* <App /> */}
</MuiThemeProvider> </MuiThemeProvider>
</ConnectedRouter> </ConnectedRouter>
</Provider>, </Provider>
</AppContainer>,
document.getElementById('app') document.getElementById('app')
) )
}
render(Master)
// Webpack Hot Module Replacement API
if (module.hot) {
module.hot.accept('components/master', () => { render(Master) })
}

View File

@@ -11,14 +11,10 @@ try {
} catch (e) { } catch (e) {
} }
var babelOptions = {
plugins: ['transform-decorators-legacy'],
presets: ['babel-polyfill', 'react', 'env', 'stage-0']
};
module.exports = { module.exports = {
entry: [ entry: [
'react-hot-loader/patch',
'webpack-hot-middleware/client',
'./src/index.tsx' './src/index.tsx'
], ],
externals: { externals: {
@@ -44,6 +40,7 @@ module.exports = {
} }
}) })
] : [ ] : [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': { 'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV), NODE_ENV: JSON.stringify(process.env.NODE_ENV),
@@ -58,6 +55,7 @@ module.exports = {
}) })
], ],
output: { output: {
publicPath: '/',
path: path.resolve(__dirname, './public'), path: path.resolve(__dirname, './public'),
filename: 'bundle-v0.1.js', filename: 'bundle-v0.1.js',
@@ -91,8 +89,7 @@ module.exports = {
test: /\.ts(x?)$/, test: /\.ts(x?)$/,
exclude: /node_modules/, exclude: /node_modules/,
use: [{ use: [{
loader: 'babel-loader', loader: 'babel-loader'
options: babelOptions
}, },
{ {
loader: 'ts-loader', loader: 'ts-loader',
@@ -109,8 +106,7 @@ module.exports = {
test: /\.js(x?)$/, test: /\.js(x?)$/,
exclude: /(node_modules|bower_components)/, exclude: /(node_modules|bower_components)/,
use: { use: {
loader: 'babel-loader', loader: 'babel-loader'
options: babelOptions
} }
}, },
{ {