Skip to content

Commit 849c747

Browse files
authored
Merge pull request #32 from Team-Hacktive/persisting-code-input
Persisting code input
2 parents 9194eb9 + ba0f611 commit 849c747

11 files changed

+175
-56
lines changed

client/components/CodeEditor.js

+36-8
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,26 @@ import PropTypes from 'prop-types'
77
import { connect } from 'react-redux'
88
import { withRouter } from 'react-router-dom'
99
import { Editor, EditorState } from 'draft-js';
10-
import { logout } from '../store'
10+
import { logout, postUserInput } from '../store'
1111
import AceEditor from 'react-ace'
1212
import brace from 'brace';
1313
import 'brace/mode/javascript';
1414
import 'brace/theme/monokai';
1515

16-
export default class CodeEditor extends Component {
16+
class CodeEditor extends Component {
1717
constructor(props) {
1818
super(props);
1919
this.onChange = this.onChange.bind(this);
2020
this.state = {
21-
code: ''
21+
code: `function add(a, b) {}`
22+
}
23+
}
24+
25+
// Receiving code input as a prop from singleProblem once that is done loading from the db api call
26+
// This has worked 100% of the time
27+
componentWillReceiveProps(nextprop){
28+
if(nextprop.userInput && nextprop.userInput.length){
29+
this.setState({code: nextprop.userInput})
2230
}
2331
}
2432

@@ -29,6 +37,8 @@ export default class CodeEditor extends Component {
2937
}
3038

3139
render() {
40+
const { problemId, userId, handleSave, userInput } = this.props;
41+
console.log('state', this.state)
3242
return (
3343
<div>
3444
<div>
@@ -41,16 +51,34 @@ export default class CodeEditor extends Component {
4151
showPrintMargin={true}
4252
showGutter={true}
4353
highlightActiveLine={true}
44-
value={`function add(a, b) { ${this.state.code} }`}
54+
value={this.state.code}
4555
setOptions={{
4656
enableBasicAutocompletion: false,
4757
enableLiveAutocompletion: true,
4858
enableSnippets: false,
4959
showLineNumbers: true,
5060
tabSize: 2,
51-
}} />
61+
}} />
5262
</div>
53-
</div>
54-
)
55-
}
63+
<button onClick={() => handleSave(problemId, userId, {savedInput: this.state.code})}>Save</button>
64+
</div>
65+
)}
5666
}
67+
68+
const mapState = (state, ownprops) => {
69+
return {
70+
userInput: ownprops.userInput,
71+
problemId: state.currentProblem.id,
72+
userId: state.user.id
73+
};
74+
};
75+
76+
const mapDispatch = dispatch => {
77+
return {
78+
handleSave(problemId, userId, input) {
79+
dispatch(postUserInput(problemId, userId, input));
80+
}
81+
};
82+
};
83+
84+
export default connect(mapState, mapDispatch)(CodeEditor);

client/components/Levels.js

-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@ const Levels = (props) => {
1414
return accum.id
1515
})
1616

17-
console.log('largestProblemId???', largestProblemId)
1817
return (
1918
<div>
2019
{
2120
//maps through all problems
2221
allProblems.length && allProblems.map(problem => {
23-
console.log('what is this', problem);
2422
return (
2523
<div key={problem.id}>
2624
{/*if the problem is less than or equal to 1 + the largest completed problem, you can click it*/}

client/components/SingleProblem.js

+26-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React, { Component } from 'react'
22
import PropTypes from 'prop-types'
33
import {connect} from 'react-redux'
4-
import {logout, getCurrentProblemThunk} from '../store'
4+
import {logout, getCurrentProblemThunk, getUserInputThunk} from '../store'
55
import { NavLink } from 'react-router-dom'
66
import Levels from './Levels'
77
import Editor from './CodeEditor'
8+
import axios from 'axios'
89

910
/**
1011
* COMPONENT
@@ -16,36 +17,48 @@ class SingleProblem extends Component {
1617
}
1718

1819
componentDidMount(){
19-
//load problem from the database
20-
this.props.loadProblem(this.props.params.match.params.id)
20+
//Find or create user <-> problem association in UserProblem table
21+
axios.get(`/api/users/${this.props.userId}/${this.props.problem.id}`)
22+
.then(res => {
23+
if(!res.data){
24+
//create association
25+
axios.post(`/api/users/${userId}/${problemId}`)
26+
.then(res => {
27+
dispatch(userProblemAssociated(res.data))
28+
})
29+
}})
30+
.catch(err => console.log(err))
31+
32+
this.props.loadProblem(this.props.params, this.props.userId)
2133
}
2234

2335
render(){
24-
const {email, isLoggedIn, problem, params} = this.props
36+
const {email, isLoggedIn, problem, params, userId, userInput} = this.props
2537
return (
2638
<div className='toc'>
27-
{/* <a href="#" onClick={handleClick}>Logout</a> */}
2839
<h3>{problem ? problem.name : ''}</h3>
2940
<h4>{problem ? problem.prompt : ''}</h4>
30-
<Editor />
41+
<Editor userInput={userInput}/>
3142
<NavLink to={'/'}>
3243
<button>Go Back Home</button>
3344
</NavLink>
3445
</div>
3546
)
3647
}
37-
3848
}
3949

4050
/**
4151
* CONTAINER
4252
*/
4353
const mapState = (state, ownprops) => {
4454
return {
45-
params: ownprops,
55+
// Params: to refactor to not use ownprops.match.params.id. Idea: put an onClick function in the Levels component to dispatch to the store
56+
params: parseInt(ownprops.match.params.id),
4657
problem: state.currentProblem,
4758
isLoggedIn: !!state.user.id,
48-
email: state.user.email
59+
email: state.user.email,
60+
userId: state.user.id,
61+
userInput: state.userInput
4962
}
5063
}
5164

@@ -54,8 +67,9 @@ const mapDispatch = (dispatch) => {
5467
handleLogOut() {
5568
dispatch(logout())
5669
},
57-
loadProblem(id) {
58-
dispatch(getCurrentProblemThunk(id))
70+
loadProblem(problemId, userId) {
71+
dispatch(getUserInputThunk(problemId, userId))
72+
dispatch(getCurrentProblemThunk(problemId, userId))
5973
}
6074
}
6175
}
@@ -65,7 +79,5 @@ export default connect(mapState, mapDispatch)(SingleProblem)
6579
/**
6680
* PROP TYPES
6781
*/
68-
SingleProblem.propTypes = {
69-
email: PropTypes.string
70-
}
82+
7183

client/components/user-home.js

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Levels from './Levels'
1010
*/
1111
export const UserHome = (props) => {
1212
const {email, isLoggedIn, handleClick, userProblems, allProblems} = props
13-
console.log("userhome renders with these props", props)
1413
return (
1514
<div className='toc'>
1615
<a href="#" onClick={handleClick}>Logout</a>

client/store/currentProblem.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ const currentProblem = []
1010
const getCurrentProblem = problem => ({type: GET_CURRENT_PROBLEM, problem})
1111

1212
//THUNKS
13-
export const getCurrentProblemThunk = (problemId) =>
14-
dispatch =>
15-
axios.get(`/api/problems/${problemId}`)
13+
export const getCurrentProblemThunk = (problemId, userId) => {
14+
return (dispatch) => {
15+
axios.get(`/api/problems/${problemId}/${userId}`)
1616
.then(res => {
1717
dispatch(getCurrentProblem(res.data))
1818
})
1919
.catch(err => console.log(err))
20+
}
21+
}
2022

2123
//REDUCER
2224
export default function(state = currentProblem, action) {

client/store/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { composeWithDevTools } from 'redux-devtools-extension'
55
import user from './user'
66
import problems from './problems'
77
import currentProblem from './currentProblem'
8+
import userInput from './userInput'
89

9-
const reducer = combineReducers({user, problems, currentProblem})
10+
const reducer = combineReducers({user, problems, currentProblem, userInput})
1011
const middleware = composeWithDevTools(applyMiddleware(
1112
thunkMiddleware,
1213
createLogger({collapsed: true})
@@ -17,3 +18,4 @@ export default store
1718
export * from './user'
1819
export * from './problems'
1920
export * from './currentProblem'
21+
export * from './userInput'

client/store/userInput.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import axios from 'axios'
2+
import user from './user';
3+
4+
//ACTION TYPES
5+
const SAVE_USER_INPUT = 'SAVE_USER_INPUT'
6+
const GET_USER_INPUT = 'GET_USER_INPUT'
7+
8+
//INITIAL STATE
9+
const userInput = ''
10+
11+
//ACTION CREATORS
12+
const saveUserInput = input => ({type: SAVE_USER_INPUT, input})
13+
const getUserInput = input => ({type: GET_USER_INPUT, input})
14+
15+
//THUNKS
16+
export const postUserInput = (problemId, userId, input) => {
17+
return (dispatch) => {
18+
axios.post(`/api/inputs/${problemId}/${userId}`, input)
19+
.then(res => {
20+
dispatch(saveUserInput(input))
21+
})
22+
.catch(err => console.log(err))
23+
}
24+
}
25+
26+
export const getUserInputThunk = (problemId, userId) => {
27+
return (dispatch) => {
28+
axios.get(`/api/inputs/${problemId}/${userId}`)
29+
.then(input => {
30+
dispatch(getUserInput(input.data.savedInput))
31+
})
32+
.catch(err => console.log(err))
33+
}
34+
}
35+
36+
//REDUCER
37+
export default function(state = userInput, action) {
38+
switch (action.type) {
39+
case SAVE_USER_INPUT:
40+
return action.input
41+
case GET_USER_INPUT:
42+
return action.input
43+
default:
44+
return state
45+
}
46+
}

server/api/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module.exports = router
44
// matches all requests to /api/{whatever}/
55
router.use('/users', require('./users'))
66
router.use('/problems', require('./problems'))
7+
router.use('/inputs', require('./inputs'))
78

89
router.use((req, res, next) => {
910
const error = new Error('Not Found')

server/api/inputs.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const router = require('express').Router()
2+
const { UserProblem } = require('../db/models')
3+
module.exports = router
4+
5+
//Get the User Input
6+
router.get('/:problemId/:userId', (req, res, next) => {
7+
UserProblem.findOne({
8+
where: {
9+
problemId: req.params.problemId,
10+
userId: req.params.userId
11+
}
12+
})
13+
.then(input => res.json(input))
14+
.catch(next)
15+
})
16+
17+
//Update the UserProblem join table
18+
router.post('/:problemId/:userId', (req, res, next) => {
19+
UserProblem.findOne({
20+
where: {
21+
problemId: req.params.problemId,
22+
userId: req.params.userId
23+
}
24+
})
25+
.then(problem => { problem.update(req.body) })
26+
.then(data => { res.sendStatus(204) })
27+
.catch(next)
28+
})
29+

server/api/problems.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const router = require('express').Router()
2-
const { Problem, Dialog, User } = require('../db/models')
2+
const { Problem, Dialog } = require('../db/models')
33
module.exports = router
44

55
//get all problems
@@ -9,16 +9,14 @@ router.get('/', (req, res, next) => {
99
.catch(next)
1010
})
1111

12-
//get a specific problem with associated dialog
13-
router.get('/:problemId', (req, res, next) => {
12+
//Get a single problem and its associated user
13+
router.get('/:problemId/:userId', (req, res, next) => {
1414
Problem.findOne({
1515
where: {
1616
id: req.params.problemId,
17-
// problemNumber: req.params.problemNumber
1817
},
19-
include: [{model: Dialog}, {model: User}]
18+
include: [{model: Dialog}]
2019
})
2120
.then(problem => res.json(problem))
2221
.catch(next)
2322
})
24-

0 commit comments

Comments
 (0)