Created
June 14, 2018 00:53
-
-
Save bradennapier/8877efb4f9e2991cedc66f57f2f3aa3c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Process } from 'redux-saga-process'; | |
import { call, put, select, fork } from 'redux-saga/effects'; | |
import { createTaskManager } from 'saga-task-manager'; | |
import appConfig from 'utils/config'; | |
import processLoadsWithScope from './config/processLoadsWithScope'; | |
import processReducer from './config/processReducer'; | |
import processSelectors from './config/processSelectors'; | |
import processActionRoutes from './config/processActionRoutes'; | |
import handleAuthStateChangedSaga from './sagas/handleAuthStateChanged'; | |
const buildConfig = config => ({ | |
pid: 'firebase', | |
reduces: 'user', | |
log: true, | |
...config, | |
}); | |
/* Private Process Selectors */ | |
const firebaseConfigSelector = state => state.config.firebase; | |
const firebaseTokenSelector = state => state.config.keys.firebase; | |
/* Wait to load until we receive AUTH_SESSION */ | |
const processLoadsOnAction = 'AUTH_SESSION'; | |
/** | |
* configureFirebaseGeneralProcess | |
* @param {Object} _config [description] | |
* @return {Process} [description] | |
*/ | |
export default function configureFirebaseGeneralProcess(_config) { | |
const config = buildConfig(_config); | |
let scope; | |
let firebase; | |
const processConfig = { | |
pid: config.pid, | |
reduces: config.reduces, | |
}; | |
class FirebaseGeneralProcess extends Process { | |
constructor(processID, prevState, proc) { | |
super(processID, prevState, proc); | |
this.state = { | |
// store the core firebase database refs | |
refs: {}, | |
// our previous state (when hot reloaded) | |
...prevState, | |
// is firebase ready to be used? | |
ready: false, | |
}; | |
} | |
// https://firebase.google.com/docs/reference/node/firebase.auth.Auth | |
* provideToken(retry = true) { | |
const fireToken = yield select(firebaseTokenSelector); | |
if (!fireToken) { | |
throw new Error( | |
'[RTDB - General]: Tried to Authenticate with Firebase but a firebase token was not found', | |
); | |
} | |
try { | |
yield call([this.state.authorizer, this.state.authorizer.signInWithCustomToken], fireToken); | |
} catch (e) { | |
yield fork([this, this.handleError], 'token', e, retry); | |
} | |
} | |
* handleFirebaseReady() { | |
// get the paths that we want to listen to globally | |
return; | |
const schema = scope.getListenerSchema(this.state.refs.dealer, { | |
states: { | |
projects: { | |
isDashPro: true, | |
isDashConnect: true, | |
isOnline: true, | |
isStatus: true, | |
}, | |
}, | |
meta: { | |
projects: true, | |
}, | |
}); | |
const handlers = { | |
onEvent: [this, this.handleFirebasePathEvent], | |
onError: [this, this.handleError], | |
}; | |
for (const listenerPath of Object.keys(schema)) { | |
const listenerSchema = schema[listenerPath]; | |
yield call( | |
[this.task, this.task.create], | |
'listeners', | |
listenerPath, | |
scope.firebasePathObserver, | |
listenerPath, | |
listenerSchema, | |
handlers, | |
); | |
} | |
} | |
* handleFirebasePathEvent( | |
[event, value, eventSchema], | |
firebasePath, | |
/* , listenerSchema , listener */ | |
) { | |
const splitPath = firebasePath.split('/'); | |
const type = `FB${splitPath.join('_').toUpperCase()}`; | |
const name = splitPath.pop(); | |
yield put({ | |
type, | |
name, | |
event, | |
value, | |
eventSchema, | |
}); | |
} | |
handleCancel() {} | |
handleError(evt, e /* , retry */) { | |
const { code, message } = e; | |
switch (code) { | |
case 'auth/invalid-custom-token': { | |
// Thrown if the custom token format is incorrect. | |
// This could also mean that the auth token has expired and | |
// must be renewed. | |
console.error('[RTDB]: Failed to Login to the Realtime Database: ', message); | |
break; | |
} | |
case 'auth/custom-token-mismatch': { | |
// Thrown if the custom token is for a different Firebase App. | |
break; | |
} | |
default: { | |
console.warn('[RTDB]: An Unknown Realtime Database Error has Occurred!'); | |
console.error(code, message); | |
break; | |
} | |
} | |
} | |
* handleFirebaseStartup() { | |
scope = this.scope; | |
firebase = scope.firebase; | |
// our process will not start until firebase is ready to be initiated and | |
// the user has authenticated . | |
if (firebase.apps.length === 0) { | |
const firebaseConfig = yield select(firebaseConfigSelector); | |
if (firebaseConfig) { | |
firebase.initializeApp(firebaseConfig); | |
} else { | |
console.error( | |
'Failed to Initiate App, Realtime Database Configuration Error [RTDB GeneralProcess]', | |
); | |
} | |
} | |
if (!this.state.authorizer) { | |
this.state.authorizer = firebase.auth(); | |
} | |
yield call([this, this.provideToken]); | |
if (!this.state.refs.db) { | |
this.state.refs.db = firebase.database().ref(appConfig('firebase.path')); | |
} | |
yield call( | |
[this.task, this.task.create], | |
'fireauth', | |
'onAuthState', | |
scope.firebaseAuthObserver, | |
this.state.authorizer, | |
'onAuthStateChanged', | |
{ | |
onEvent: [this, handleAuthStateChangedSaga], | |
onCancel: [this, this.handleCancel], | |
onError: [this, this.handleError], | |
}, | |
); | |
} | |
/* When we want to logout of the Firebase we call this. | |
*/ | |
* handleFirebaseLogout() { | |
if (this.state.authorizer) { | |
this.state.authorizer.signOut(); | |
yield put({ | |
type: 'FIREBASE_LOGOUT', | |
}); | |
} | |
this.state.ready = false; | |
} | |
/* When our auth observer indicates we lost authentication this will | |
be called | |
*/ | |
handleFirebaseLoggedOut() { | |
console.warn('[RTDB] - USER LOGGED OUT!'); | |
} | |
* processStarts(processID) { | |
scope = this.scope; | |
firebase = scope.firebase; | |
this.task = createTaskManager(processID, { | |
name: 'DASH_FIREBASE', | |
log: config.log, | |
icon: '☀️', | |
}); | |
yield call([this, this.handleAuthSession]); | |
} | |
* handleAuthSession() { | |
yield call([this, this.handleFirebaseStartup]); | |
} | |
/** | |
* handleUserLogout | |
* Called by handleAuthStateChangedSaga when the | |
* user has logged out and when a signout is | |
* detected so that we can logout the user. | |
*/ | |
* handleUserLogout() { | |
yield call([this, this.handleFirebaseLogout]); | |
yield call([this.task, this.task.cancelAll]); | |
} | |
} | |
return { | |
process: FirebaseGeneralProcess, | |
config: processConfig, | |
selectors: processSelectors, | |
actionRoutes: processActionRoutes, | |
reducer: processReducer, | |
loadOnAction: processLoadsOnAction, | |
loadProcess: processLoadsWithScope, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment