diff --git a/docs/roadmap.md b/docs/roadmap.md index e46c8d96c..966c1588a 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -49,6 +49,9 @@ #### Enhancements/Fixes * Return correct promise from `firebase.auth().signOut()` - [#152](https://github.com/prescottprue/react-redux-firebase/issues/152) * Removed `browser` field from `package.json` so that webpack will point to `main` field - [#128](https://github.com/prescottprue/react-redux-firebase/issues/128) +* Fix `uniqueSet` race condition - [#207](https://github.com/prescottprue/react-redux-firebase/issues/207) +* improved testing of `firebaseConnect` HOC +* Implement [`firebase-server`](https://github.com/urish/firebase-server) for tests instead of using demo firebase instance ## Future Minor Versions (`v1.6.0 - v1.*.*`) @@ -60,25 +63,15 @@ #### Features * Config option for populated items updating when changed - [#69](https://github.com/prescottprue/react-redux-firebase/issues/69) * Expose whole Firebase instance (warning: Using Firebase instance methods will not dispatch actions or update redux state) -* Config option to not remove all data on logout (potential config syntax: `preserveOnLogout: ['todos']`) -* Integration for [`react-native-firebase`](https://github.com/invertase/react-native-firebase) for using Firebase native modules instead of JS library (allowing for instance to be passed in). +* Config option to not remove all data on logout (already in `v2.*.*`) * Setting that allows for `waitForPopulate` to be turned off (i.e. return populated data as in becomes available). As of `v1.4.0-rc.2`, populate only sets `isLoaded` to true after all children are loaded, `waitForPopulate` would make this optional - [#121](https://github.com/prescottprue/react-redux-firebase/issues/121) * Integration for [`react-native-google-signin`](https://github.com/devfd/react-native-google-signin) to simplify react-native authentication implementation * Nested populates - [#85](https://github.com/prescottprue/react-redux-firebase/issues/85) +* Renaming a file on upload (currently does not work due to HTML 5 File element being read only) #### Enhancements/Fixes *None Yet Planned* -## Future Minor Versions (`v1.*.*`) - -**Note:** Subject to change - -#### Breaking Changes - *None Yet Planned* - -#### Features -* Nested populates [#85](https://github.com/prescottprue/react-redux-firebase/issues/85)) - ## Upcoming Major Version (`v2.0.0`) **NOTE:** The changes are unconfirmed and will most likely change @@ -95,13 +88,16 @@ * Improved rendering/update performance for `react` - [#84](https://github.com/prescottprue/react-redux-firebase/issues/84) #### Features +* Integration for [`react-native-firebase`](https://github.com/invertase/react-native-firebase) for using Firebase native modules instead of JS library (allowing for instance to be passed in). * Support for keeping data on logout - [#125](https://github.com/prescottprue/react-redux-firebase/issues/125) +* Online users/presence functionality based on [firebase's presence example](http://firebase.googleblog.com/2013/06/how-to-build-presence-system.html) * `react-native` index file referenced in `package.json` that makes it no longer necessary to pass `ReactNative` in config * `AuthRequired` decorator (or decorator factory) that forces auth to exist before rendering component * Support native modules through [`react-native-firebase`](https://github.com/invertase/react-native-firebase) [#131](https://github.com/prescottprue/react-redux-firebase/issues/131) +* Config option to not remove all data on logout (potential config syntax: `preserveOnLogout: ['todos']`) #### Enhancements/Fixes -* Implement [`firebase-server`](https://github.com/urish/firebase-server) for tests instead of using demo firebase instance + #### Under Consideration * Possibility of delayed initialization - [#70](https://github.com/prescottprue/react-redux-firebase/issues/70) (more research needed) diff --git a/index.d.ts b/index.d.ts index a09f298ff..75dcf84d3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -105,9 +105,9 @@ export interface listenerConfigFunc { export function buildChildList(data: any, list: any, p: any): any; -export function customToJS(data: any, path: any, custom: any, notSetValue: any): any; +export function customToJS(data: any, path: any, custom: any, notSetValue?: any): any; -export function dataToJS(data: any, path: any, notSetValue: any): any; +export function dataToJS(data: any, path: any, notSetValue?: any): any; export function firebase(...args: any[]): any; @@ -123,11 +123,11 @@ export function isEmpty(data: any): any; export function isLoaded(...args: any[]): any; -export function orderedToJS(data: any, path: any, notSetValue: any): any; +export function orderedToJS(data: any, path: any, notSetValue?: any): any; -export function pathToJS(data: any, path: any, notSetValue: any): any; +export function pathToJS(data: any, path: any, notSetValue?: any): any; -export function populatedDataToJS(data: any, path: any, populates: any, notSetValue: any): any; +export function populatedDataToJS(data: any, path: any, populates: any, notSetValue?: any): any; export function reactReduxFirebase(fbConfig: ConfigObject, otherConfig: any, ...args: any[]): any; @@ -188,9 +188,9 @@ export namespace getFirebase { export namespace helpers { function buildChildList(data: any, list: any, p: any): any; - function customToJS(data: any, path: any, custom: any, notSetValue: any): any; + function customToJS(data: any, path: any, custom: any, notSetValue?: any): any; - function dataToJS(data: any, path: any, notSetValue: any): any; + function dataToJS(data: any, path: any, notSetValue?: any): any; function fixPath(path: any): any; @@ -198,11 +198,11 @@ export namespace helpers { function isLoaded(...args: any[]): any; - function orderedToJS(data: any, path: any, notSetValue: any): any; + function orderedToJS(data: any, path: any, notSetValue?: any): any; - function pathToJS(data: any, path: any, notSetValue: any): any; + function pathToJS(data: any, path: any, notSetValue?: any): any; - function populatedDataToJS(data: any, path: any, populates: any, notSetValue: any): any; + function populatedDataToJS(data: any, path: any, populates: any, notSetValue?: any): any; function toJS(data: any): any; diff --git a/package.json b/package.json index 3a2edc94f..bcb16ccea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-redux-firebase", - "version": "1.5.0-rc.2", + "version": "1.5.0-rc.3", "description": "Redux integration for Firebase. Comes with a Higher Order Component for use with React.", "main": "lib/index.js", "module": "es/index.js", @@ -102,7 +102,7 @@ "eslint-plugin-node": "^4.2.2", "eslint-plugin-promise": "^3.5.0", "eslint-plugin-react": "^6.10.3", - "eslint-plugin-standard": "^2.2.0", + "eslint-plugin-standard": "^3.0.0", "firebase-server": "^0.10.1", "gitbook-cli": "^2.3.0", "istanbul": "^1.1.0-alpha.1", diff --git a/src/actions/auth.js b/src/actions/auth.js index add26ee50..72ee013a1 100644 --- a/src/actions/auth.js +++ b/src/actions/auth.js @@ -198,14 +198,17 @@ export const createUserProfile = (dispatch, firebase, userData, profile) => { return Promise.resolve(userData) } const { database, _: { config } } = firebase - if (isFunction(config.profileFactory)) { - profile = config.profileFactory(userData, profile) - } - if (isFunction(config.profileDecorator)) { - if (isFunction(console.warn)) { // eslint-disable-line no-console + try { + if (isFunction(config.profileFactory)) { + profile = config.profileFactory(userData, profile) + } + if (isFunction(config.profileDecorator)) { console.warn('profileDecorator is Depreceated and will be removed in future versions. Please use profileFactory.') // eslint-disable-line no-console + profile = config.profileDecorator(userData, profile) } - profile = config.profileDecorator(userData, profile) + } catch (err) { + console.error('Error occured within profileFactory function:', err.toString ? err.toString() : err) // eslint-disable-line no-console + return Promise.reject(err) } // Check for user's profile at userProfile path if provided return database() diff --git a/src/compose.js b/src/compose.js index 2c18cd3aa..417d84ce2 100644 --- a/src/compose.js +++ b/src/compose.js @@ -280,7 +280,8 @@ export default (fbConfig, otherConfig) => next => /** * @private * @description Sets data to Firebase only if the path does not already - * exist, otherwise it rejects. + * exist, otherwise it rejects. Internally uses a Firebase transaction to + * prevent a race condition between seperate clients calling uniqueSet. * @param {String} path - Path to location on Firebase which to set * @param {Object|String|Boolean|Number} value - Value to write to Firebase * @param {Function} onComplete - Function to run on complete (`not required`) @@ -298,14 +299,15 @@ export default (fbConfig, otherConfig) => next => */ const uniqueSet = (path, value, onComplete) => rootRef.child(path) - .once('value') - .then(snap => { - if (snap.val && snap.val() !== null) { - const err = new Error('Path already exists.') - if (onComplete) onComplete(err) - return Promise.reject(err) + .transaction(d => d === null ? value : undefined) + .then(({ commited, snapshot }) => { + if (!commited) { + const newError = new Error('Path already exists.') + if (onComplete) onComplete(newError) + return Promise.reject(newError) } - return rootRef.child(path).set(value, onComplete) + if (onComplete) onComplete(snapshot) + return snapshot }) /** diff --git a/tests/unit/connect.spec.js b/tests/unit/connect.spec.js index 4025bfbdb..b71d4daf7 100644 --- a/tests/unit/connect.spec.js +++ b/tests/unit/connect.spec.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types' import ReactDOM from 'react-dom' import TestUtils from 'react-addons-test-utils' import { createStore, compose, combineReducers } from 'redux' -import getDisplayName from 'react-display-name' import reactReduxFirebase from '../../src/compose' import firebaseConnect from '../../src/firebaseConnect' diff --git a/tests/unit/firebaseConnect.spec.js b/tests/unit/firebaseConnect.spec.js index be58ee4e5..555e40b20 100644 --- a/tests/unit/firebaseConnect.spec.js +++ b/tests/unit/firebaseConnect.spec.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types' import ReactDOM from 'react-dom' import TestUtils from 'react-addons-test-utils' import { createStore, compose, combineReducers } from 'redux' -import getDisplayName from 'react-display-name' import reactReduxFirebase from '../../src/compose' import firebaseConnect, { createFirebaseConnect } from '../../src/firebaseConnect'