From 69d04100e72cf3b3b0038c8f64691155b6c8dd2c Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 31 Jan 2018 17:07:13 -0800 Subject: [PATCH 01/14] Switch to using react-redux-resource imports --- package.json | 2 +- src/components/CreateGist.js | 2 +- src/components/Gist.js | 2 +- src/components/Gists.js | 2 +- src/state/gists/action-creators.js | 2 +- src/state/gists/reducer.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3bcb15a..d9ee651 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,10 @@ "react": "^16.0.0", "react-dom": "^16.0.0", "react-redux": "^5.0.5", + "react-redux-resource": "0.0.1", "react-router-dom": "^4.1.1", "react-scripts": "1.0.10", "redux": "^3.7.1", - "redux-resource": "^2.4.0", "redux-resource-plugins": "^2.1.0", "redux-resource-xhr": "^3.0.0", "redux-thunk": "^2.2.0", diff --git a/src/components/CreateGist.js b/src/components/CreateGist.js index 30f368e..efe5bcf 100644 --- a/src/components/CreateGist.js +++ b/src/components/CreateGist.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; -import { getStatus, getResources } from 'redux-resource'; +import { getStatus, getResources } from 'react-redux-resource'; import { createGist, resetCreateGistStatus } from '../state/gists/action-creators'; class CreateGist extends Component { diff --git a/src/components/Gist.js b/src/components/Gist.js index f023a81..e97444a 100644 --- a/src/components/Gist.js +++ b/src/components/Gist.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import { getStatus } from 'redux-resource'; +import { getStatus } from 'react-redux-resource'; import _ from 'lodash'; import './Gist.css'; import { diff --git a/src/components/Gists.js b/src/components/Gists.js index 17f8f0c..303ef31 100644 --- a/src/components/Gists.js +++ b/src/components/Gists.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; -import { getResources, getStatus } from 'redux-resource'; +import { getResources, getStatus } from 'react-redux-resource'; import './Gists.css'; import { readManyUsersGists } from '../state/gists/action-creators'; import login from '../personal-access-token'; diff --git a/src/state/gists/action-creators.js b/src/state/gists/action-creators.js index e79e40e..c19764c 100644 --- a/src/state/gists/action-creators.js +++ b/src/state/gists/action-creators.js @@ -1,4 +1,4 @@ -import { actionTypes } from 'redux-resource'; +import { actionTypes } from 'react-redux-resource'; import { crudRequest } from 'redux-resource-xhr'; import headers from '../../utils/headers'; diff --git a/src/state/gists/reducer.js b/src/state/gists/reducer.js index 1fb0c89..6606006 100644 --- a/src/state/gists/reducer.js +++ b/src/state/gists/reducer.js @@ -1,4 +1,4 @@ -import { resourceReducer } from 'redux-resource'; +import { resourceReducer } from 'react-redux-resource'; import { httpStatusCodes } from 'redux-resource-plugins'; export default resourceReducer('gists', { From 3ad093281661ed179558e5c7d899bc0bd07e1c15 Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 31 Jan 2018 17:15:07 -0800 Subject: [PATCH 02/14] Switch to using ResourceRequest for Gists page --- .prettierrc | 4 ++ src/components/App.js | 5 +- src/components/Gists.js | 100 +++++++++-------------------- src/index.js | 28 ++++---- src/request-components/Gists.js | 23 +++++++ src/state/gists/action-creators.js | 96 ++++++++++++--------------- 6 files changed, 114 insertions(+), 142 deletions(-) create mode 100644 .prettierrc create mode 100644 src/request-components/Gists.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ee4373a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "jsxBracketSameLine": true +} diff --git a/src/components/App.js b/src/components/App.js index 6554425..d3bb873 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -21,10 +21,9 @@ class App extends Component { New Gist - {!isLoggedIn && ( + {!isLoggedIn && `Please provide a GitHub Personal Access Token to use this application. - For more, refer to this project's documentation on GitHub.` - )} + For more, refer to this project's documentation on GitHub.`} {isLoggedIn && children} ); diff --git a/src/components/Gists.js b/src/components/Gists.js index 303ef31..732b3c8 100644 --- a/src/components/Gists.js +++ b/src/components/Gists.js @@ -1,79 +1,37 @@ import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; -import { getResources, getStatus } from 'react-redux-resource'; import './Gists.css'; -import { readManyUsersGists } from '../state/gists/action-creators'; +import { ReadUsersGists } from '../request-components/Gists'; import login from '../personal-access-token'; const username = login.username; -class Gists extends Component { - render() { - const { usersGists, usersGistsStatus } = this.props; - - return ( -
- {usersGistsStatus.pending && ('Loading gists...')} - {usersGistsStatus.failed && ( - - There was an error loading gists. - - )} - {usersGistsStatus.succeeded && ( -
    - {usersGists.map(gist => ( -
  • - {username} /  - - {Object.keys(gist.files)[0]} - -   - {!gist.public && '🔒'} -
  • - ))} -
- )} -
- ); - } - - componentDidMount() { - this.fetchUsersGists(); - } - - componentWillUnmount() { - if (this.readManyUsersGistsXhr) { - this.readManyUsersGistsXhr.abort(); - } - } - - fetchUsersGists = () => { - const { readManyUsersGists } = this.props; - - if (this.readManyUsersGistsXhr) { - this.readManyUsersGistsXhr.abort(); - } - - this.readManyUsersGistsXhr = readManyUsersGists(username); - } +export default function Gists() { + return ( + + {({ status, lists, doFetch }) => ( +
+ {status.pending && 'Loading gists...'} + {status.failed && ( + + There was an error loading gists.{' '} + + + )} + {status.succeeded && ( +
    + {lists.usersGists.map(gist => ( +
  • + {username} /  + {Object.keys(gist.files)[0]} +   + {!gist.public && '🔒'} +
  • + ))} +
+ )} +
+ )} +
+ ); } - -function mapStateToProps(state) { - const usersGists = getResources(state.gists, 'usersGists'); - const usersGistsStatus = getStatus(state, 'gists.requests.getUsersGists.status', true); - - return { - usersGists, - usersGistsStatus - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - readManyUsersGists - }, dispatch); -} - -export default connect(mapStateToProps, mapDispatchToProps)(Gists); diff --git a/src/index.js b/src/index.js index 8b7dcd5..2e26d62 100644 --- a/src/index.js +++ b/src/index.js @@ -9,18 +9,22 @@ import Gist from './components/Gist'; import CreateGist from './components/CreateGist'; import store from './state/store'; -ReactDOM.render(( +ReactDOM.render( - ( - - - - - - - - )}/> + ( + + + + + + + + )} + /> - -), document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/src/request-components/Gists.js b/src/request-components/Gists.js new file mode 100644 index 0000000..0a18f90 --- /dev/null +++ b/src/request-components/Gists.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { ResourceRequest, Fetch } from 'react-redux-resource'; +import headers from '../utils/headers'; + +export function ReadUsersGists({ username, children }) { + const request = ( + + ); + + return ( + + ); +} diff --git a/src/state/gists/action-creators.js b/src/state/gists/action-creators.js index c19764c..0145540 100644 --- a/src/state/gists/action-creators.js +++ b/src/state/gists/action-creators.js @@ -21,16 +21,17 @@ export function createGist(gist) { headers }; - return dispatch => crudRequest('create', { - actionDefaults: { - resourceName: 'gists', - request: 'createGist', - list: 'createdGists', - }, - transformData: singleResourceToArray, - xhrOptions, - dispatch - }); + return dispatch => + crudRequest('create', { + actionDefaults: { + resourceName: 'gists', + request: 'createGist', + list: 'createdGists' + }, + transformData: singleResourceToArray, + xhrOptions, + dispatch + }); } export function resetCreateGistStatus() { @@ -49,35 +50,16 @@ export function readGist(gistId) { headers }; - return dispatch => crudRequest('read', { - actionDefaults: { - resourceName: 'gists', - resources: [gistId], - }, - transformData: singleResourceToArray, - xhrOptions, - dispatch - }); -} - -export function readManyUsersGists(username) { - const xhrOptions = { - method: 'GET', - url: `https://api.github.com/users/${username}/gists`, - json: true, - headers - }; - - return dispatch => crudRequest('read', { - actionDefaults: { - resourceName: 'gists', - request: 'getUsersGists', - list: 'usersGists', - mergeListIds: false, - }, - xhrOptions, - dispatch - }); + return dispatch => + crudRequest('read', { + actionDefaults: { + resourceName: 'gists', + resources: [gistId] + }, + transformData: singleResourceToArray, + xhrOptions, + dispatch + }); } export function updateGist(gistId, gist) { @@ -89,15 +71,16 @@ export function updateGist(gistId, gist) { headers }; - return dispatch => crudRequest('update', { - actionDefaults: { - resourceName: 'gists', - resources: [gistId], - }, - transformData: singleResourceToArray, - xhrOptions, - dispatch - }); + return dispatch => + crudRequest('update', { + actionDefaults: { + resourceName: 'gists', + resources: [gistId] + }, + transformData: singleResourceToArray, + xhrOptions, + dispatch + }); } export function resetUpdateGistStatus(gistId) { @@ -115,12 +98,13 @@ export function deleteGist(gistId) { headers }; - return dispatch => crudRequest('delete', { - actionDefaults: { - resourceName: 'gists', - resources: [gistId], - }, - xhrOptions, - dispatch - }); + return dispatch => + crudRequest('delete', { + actionDefaults: { + resourceName: 'gists', + resources: [gistId] + }, + xhrOptions, + dispatch + }); } From 336832a2363ab002103e4895e3296360bad93336 Mon Sep 17 00:00:00 2001 From: Jmeas Date: Fri, 2 Feb 2018 19:07:47 -0800 Subject: [PATCH 03/14] Use React Redux Resource for gist creation --- src/components/CreateGist.js | 110 ----------------------------- src/components/Gists.js | 2 +- src/components/NewGist.js | 82 +++++++++++++++++++++ src/index.js | 4 +- src/request-components/Gists.js | 22 ++++++ src/state/gists/action-creators.js | 30 -------- 6 files changed, 107 insertions(+), 143 deletions(-) delete mode 100644 src/components/CreateGist.js create mode 100644 src/components/NewGist.js diff --git a/src/components/CreateGist.js b/src/components/CreateGist.js deleted file mode 100644 index efe5bcf..0000000 --- a/src/components/CreateGist.js +++ /dev/null @@ -1,110 +0,0 @@ -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; -import { getStatus, getResources } from 'react-redux-resource'; -import { createGist, resetCreateGistStatus } from '../state/gists/action-creators'; - -class CreateGist extends Component { - render() { - const { createGistStatus, createdGist } = this.props; - const { description } = this.state; - - return ( -
- {createGistStatus.succeeded && ( -
- Your gist was successfully created. - {' '} - - Go to Gist details. - -
- )} - {!createGistStatus.succeeded && ( -
-
- - {createGistStatus.pending && 'Creating gist...'} - {createGistStatus.failed && 'An error occurred while creating the gist.'} -
-
-
- Description: -
- -
-
- )} -
- ); - } - - state = { - description: '', - files: {} - }; - - componentWillUnmount() { - const { resetCreateGistStatus } = this.props; - - if (this.createGistXhr) { - this.createGistXhr.abort(); - } - - // We need to reset whatever the current status of the create request is - // when we navigate away. That way, users can create new gists when they - // return. - resetCreateGistStatus(); - } - - createGist = () => { - const { createGist } = this.props; - const { description } = this.state; - - this.createGistXhr = createGist({ - description, - public: true, - files: { - 'file1.txt': { - content: 'String file contents' - } - } - }); - } - - onDescriptionChange = (event) => { - this.setState({ - description: event.target.value - }); - } -} - -function mapStateToProps(state, props) { - const createGistStatus = getStatus(state, 'gists.requests.createGist.status'); - const createdGist = getResources(state.gists, 'createdGists')[0]; - - return { - createdGist, - createGistStatus - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ - createGist, - resetCreateGistStatus - }, dispatch); -} - -export default connect(mapStateToProps, mapDispatchToProps)(CreateGist); diff --git a/src/components/Gists.js b/src/components/Gists.js index 732b3c8..0f1828f 100644 --- a/src/components/Gists.js +++ b/src/components/Gists.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Link } from 'react-router-dom'; import './Gists.css'; import { ReadUsersGists } from '../request-components/Gists'; diff --git a/src/components/NewGist.js b/src/components/NewGist.js new file mode 100644 index 0000000..e8bcb40 --- /dev/null +++ b/src/components/NewGist.js @@ -0,0 +1,82 @@ +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; +import { CreateGist } from '../request-components/Gists'; +import { resetCreateGistStatus } from '../state/gists/action-creators'; + +export default class NewGist extends Component { + render() { + const { description } = this.state; + + return ( + + {({ status, lists, doFetch }) => ( +
+ {status.succeeded && ( +
+ Your gist was successfully created.{' '} + + Go to Gist details. + +
+ )} + {!status.succeeded && ( +
+
+ + {status.pending && 'Creating gist...'} + {status.failed && + 'An error occurred while creating the gist.'} +
+
+
Description:
+ +
+
+ )} +
+ )} +
+ ); + } + + state = { + description: '', + files: {} + }; + + getCreateRequestBody = () => { + const { createGist } = this.props; + const { description } = this.state; + + const gist = { + description, + public: true, + files: { + 'file1.txt': { + content: 'String file contents' + } + } + }; + + return { + body: JSON.stringify(gist) + }; + }; + + onDescriptionChange = event => { + this.setState({ + description: event.target.value + }); + }; +} diff --git a/src/index.js b/src/index.js index 2e26d62..53dbeaa 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import './index.css'; import App from './components/App'; import Gists from './components/Gists'; import Gist from './components/Gist'; -import CreateGist from './components/CreateGist'; +import NewGist from './components/NewGist'; import store from './state/store'; ReactDOM.render( @@ -18,7 +18,7 @@ ReactDOM.render( - + diff --git a/src/request-components/Gists.js b/src/request-components/Gists.js index 0a18f90..435dc53 100644 --- a/src/request-components/Gists.js +++ b/src/request-components/Gists.js @@ -2,6 +2,12 @@ import React from 'react'; import { ResourceRequest, Fetch } from 'react-redux-resource'; import headers from '../utils/headers'; +// The Redux Resource XHR library only exports bulk actions, so we use this +// function to turn single-resource responses from the server into arrays. +function singleResourceToArray(body) { + return [body]; +} + export function ReadUsersGists({ username, children }) { const request = ( ); } + +export function CreateGist({ children }) { + const request = ( + + ); + + return ( + + ); +} diff --git a/src/state/gists/action-creators.js b/src/state/gists/action-creators.js index 0145540..285b7f8 100644 --- a/src/state/gists/action-creators.js +++ b/src/state/gists/action-creators.js @@ -12,36 +12,6 @@ function singleResourceToArray(body) { return [body]; } -export function createGist(gist) { - const xhrOptions = { - method: 'POST', - url: 'https://api.github.com/gists', - json: true, - body: gist, - headers - }; - - return dispatch => - crudRequest('create', { - actionDefaults: { - resourceName: 'gists', - request: 'createGist', - list: 'createdGists' - }, - transformData: singleResourceToArray, - xhrOptions, - dispatch - }); -} - -export function resetCreateGistStatus() { - return { - type: actionTypes.CREATE_RESOURCES_NULL, - resourceName: 'gists', - request: 'createGist' - }; -} - export function readGist(gistId) { const xhrOptions = { method: 'GET', From 704e0e779e94d062062b3be6067f90714801537e Mon Sep 17 00:00:00 2001 From: Jmeas Date: Sat, 3 Feb 2018 09:28:38 -0800 Subject: [PATCH 04/14] Remove unused createGist variables --- src/components/NewGist.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/NewGist.js b/src/components/NewGist.js index e8bcb40..c2c8d79 100644 --- a/src/components/NewGist.js +++ b/src/components/NewGist.js @@ -1,7 +1,6 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import { CreateGist } from '../request-components/Gists'; -import { resetCreateGistStatus } from '../state/gists/action-creators'; export default class NewGist extends Component { render() { @@ -56,7 +55,6 @@ export default class NewGist extends Component { }; getCreateRequestBody = () => { - const { createGist } = this.props; const { description } = this.state; const gist = { From 40dc56b2faa573c3066c1fdfc8ae2f89e9cffdd9 Mon Sep 17 00:00:00 2001 From: Jmeas Date: Sat, 3 Feb 2018 12:05:20 -0800 Subject: [PATCH 05/14] Switch to React bindings for gist page --- package.json | 1 + src/components/Gist.js | 238 ++++++++++++----------------- src/request-components/Gists.js | 56 +++++++ src/state/gists/action-creators.js | 76 +-------- src/state/gists/reducer.js | 2 - src/state/store.js | 2 +- 6 files changed, 161 insertions(+), 214 deletions(-) diff --git a/package.json b/package.json index d9ee651..1b8871d 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "lodash": "^4.17.4", "react": "^16.0.0", + "react-composer": "^3.0.1", "react-dom": "^16.0.0", "react-redux": "^5.0.5", "react-redux-resource": "0.0.1", diff --git a/src/components/Gist.js b/src/components/Gist.js index e97444a..1c591ff 100644 --- a/src/components/Gist.js +++ b/src/components/Gist.js @@ -1,69 +1,69 @@ import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { getStatus } from 'react-redux-resource'; +import Composer from 'react-composer'; import _ from 'lodash'; import './Gist.css'; -import { - readGist, updateGist, deleteGist, resetUpdateGistStatus -} from '../state/gists/action-creators'; +import { ReadGist, UpdateGist, DeleteGist } from '../request-components/Gists'; -class Gist extends Component { +// Note: see the bottom of the file for where this gets connected to the +// Redux store + +export class Gist extends Component { render() { - const { - readGistStatus, deleteGistStatus, updateGistStatus, gistNotFound - } = this.props; + const { gistId, requests } = this.props; + const [readGist, updateGist, deleteGist] = requests; const { description, files } = this.state; - // When the Gist is successfully deleted, this render function may be called - // once, or a handful of times. We can just return `null` until the page - // transition occurs. - if (deleteGistStatus.succeeded) { - return null; - } + const gist = readGist.resources[gistId]; - const changePending = updateGistStatus.pending || deleteGistStatus.pending; + const gistNotFound = readGist.request.statusCode === 404; + const changePending = + updateGist.status.pending || deleteGist.status.pending; return (
- {readGistStatus.pending && ('Loading gist...')} - {readGistStatus.failed && !gistNotFound && ( - - There was an error while retrieving this gist. - - )} - {readGistStatus.failed && gistNotFound && ('This gist could not be found.')} - {readGistStatus.succeeded && ( + {!gist && readGist.status.pending && 'Loading gist...'} + {!gist && + readGist.status.failed && + !gistNotFound && ( + + There was an error while retrieving this gist.{' '} + + + )} + {!gist && + readGist.status.failed && + gistNotFound && + 'This gist could not be found.'} + {gist && (
- {updateGistStatus.pending && 'Saving gist...'} - {updateGistStatus.succeeded && 'Saved!'} - {deleteGistStatus.pending && 'Deleting gist...'} + {updateGist.status.pending && 'Saving gist...'} + {updateGist.status.succeeded && 'Saved!'} + {deleteGist.status.pending && 'Deleting gist...'}
-
- Description: -
+
Description:
+ onChange={this.onDescriptionChange} + />
{_.map(files, (file, originalFilename) => { @@ -73,12 +73,17 @@ class Gist extends Component { type="text" className="gist_fileNameInput" value={file.filename} - onChange={(event) => this.onFileNameChange(originalFilename, event)}/> + onChange={event => + this.onFileNameChange(originalFilename, event) + } + />