diff --git a/.eslintrc b/.eslintrc index da00d08..211e3eb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -53,7 +53,7 @@ "no-constant-condition": 0, // disallow use of constant expressions in conditions "no-control-regex": 0, // disallow control characters in regular expressions "no-debugger": 1, // disallow use of debugger - "no-dupe-keys": 1, // disallow duplicate keys when creating object literals + "no-dupe-keys": 0, // disallow duplicate keys when creating object literals "no-empty": 1, // disallow empty statements "no-empty-class": 0, // disallow the use of empty character classes in regular expressions "no-ex-assign": 1, // disallow assigning to the exception in a catch block diff --git a/browser/js/components/Annotator/actions.js b/browser/js/components/Annotator/actions.js index e66144d..e6b2a47 100644 --- a/browser/js/components/Annotator/actions.js +++ b/browser/js/components/Annotator/actions.js @@ -1,7 +1,12 @@ 'use strict' +import axios from 'axios' +import * as api from './apiActions' + export const SELECTION = 'SELECTION' export const ANNOTATION = 'ANNOTATION' +export const ANNOTATION_CLEAR = 'ANNOTATION_CLEAR' +export const ANNOTATION_ADDED = 'ANNOTATION_ADDED' export function selection(_selection){ @@ -12,12 +17,37 @@ export function selection(_selection){ } } -export function annotation(_selection, _annotation){ +export function annotation(_annotation){ + let {selection, annotation, location} = _annotation; + return api.postAnnotation({selection, annotation, location}) +} + +export function finishAnnotation(payload){ + let finisher = payload; + finisher.added = false; return { type: ANNOTATION, - annotation: _annotation, - selection: _selection, - selectionString: _selection.toString(), - added: false + payload: payload + } +} + +export function clearAnnotation( added = false, selection = null, annotation = null ){ + return { + type: ANNOTATION_CLEAR, + payload: { + added, + annotation, + selection, + selectionString: !selection ? null : selection.toString() } } } + +export function annotationAdded( added = true, clear = false ){ + if(clear) clearAnnotation( added, null, null ); + return { + type: ANNOTATION_ADDED, + payload: { + added + } + } +} diff --git a/browser/js/components/Annotator/apiActions.js b/browser/js/components/Annotator/apiActions.js new file mode 100644 index 0000000..2e89af3 --- /dev/null +++ b/browser/js/components/Annotator/apiActions.js @@ -0,0 +1,148 @@ +'use strict'; + +import axios from 'axios'; +import APIROUTES from '../../utils/apiRoutes' +import {cloneDeep as _cloneDeep} from 'lodash'; +import {getXPath} from 'xpath-dom'; +import {finishAnnotation} from './actions' + +export const LOAD_ANNOTATIONS_REQUEST = 'LOAD_ANNOTATIONS_REQUEST' +export const LOAD_ANNOTATIONS_SUCCESS = 'LOAD_ANNOTATIONS_SUCCESS' +export const LOAD_ANNOTATIONS_FAILURE = 'LOAD_ANNOTATIONS_FAILURE' +export const CREATE_ANNOTATION_REQUEST = 'CREATE_ANNOTATION_REQUEST' +export const CREATE_ANNOTATION_SUCCESS = 'CREATE_ANNOTATION_SUCCESS' +export const CREATE_ANNOTATION_FAILURE = 'CREATE_ANNOTATION_FAILURE' +export const LOAD_ANNOTATION_REQUEST = 'LOAD_ANNOTATION_REQUEST' +export const LOAD_ANNOTATION_SUCCESS = 'LOAD_ANNOTATION_SUCCESS' +export const LOAD_ANNOTATION_FAILURE = 'LOAD_ANNOTATION_FAILURE' +export const UPDATE_ANNOTATION_REQUEST = 'UPDATE_ANNOTATION_REQUEST' +export const UPDATE_ANNOTATION_SUCCESS = 'UPDATE_ANNOTATION_SUCCESS' +export const UPDATE_ANNOTATION_FAILURE = 'UPDATE_ANNOTATION_FAILURE' + + +export const getUserAnnotations = + userId => + dispatch => { + dispatch(LOAD_ANNOTATIONS_REQUEST) + return axios.get(APIROUTES.annotationsByUserId(userId)) + .then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_ANNOTATIONS_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_ANNOTATIONS_FAILURE, + payload: err + })) } + +export const getAnnotationById = + annotationId => + dispatch => { + dispatch(LOAD_ANNOTATION_REQUEST) + return axios.get(APIROUTES.annotationById(annotationId)) + .then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_ANNOTATION_REQUEST, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_ANNOTATION_FAILURE, + payload: err + }))} + +export const getAnnotationsByUserTest = + userTestId => + dispatch => { + dispatch(LOAD_ANNOTATIONS_REQUEST) + return axios.get(APIROUTES.annotationsByUserTest(userTestId)) + .then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_ANNOTATIONS_REQUEST, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_ANNOTATIONS_FAILURE, + payload: err + }))} + +export const postAnnotation = + annotation => + (dispatch, getState) =>{ + let selection = annotation.selection; + let payload = { + route: APIROUTES.annotation, + anchorNode: getXPath(selection.anchorNode), + anchorOffset: selection.anchorOffset, + focusNode: getXPath(selection.focusNode), + focusOffset: selection.focusOffset, + rangeCount: selection.rangeCount, + selectionString: selection.toString(), + color: annotation.color, + location: annotation.location + } + dispatch({ + type: CREATE_ANNOTATION_REQUEST, + payload + }) + return axios.post(APIROUTES.annotation, payload) + .then(res => res.data) + .then(resData => { + dispatch({ + type: CREATE_ANNOTATION_SUCCESS, + payload: resData + }) + payload.selection = annotation.selection; + payload.resData = resData; + return dispatch(finishAnnotation(payload)) + } + ) + .catch( err => dispatch({ + type: CREATE_ANNOTATION_FAILURE, + payload: err + }))} + + +export const postAnnotationByUserTest = + (annotation, userTestId) => + (dispath, getState) =>{ + dispatch(CREATE_ANNOTATION_REQUEST) + return axios.post(APIROUTES.annotationsByUserTest(userTestId), annotation) + .then( res => res.data) + .then(resData => dispatch({ + type: CREATE_ANNOTATION_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: CREATE_ANNOTATION_FAILURE, + payloard: err + }))} + +export const updateAnnotation = + (annotation, annotationId) => + (dispatch, getState) =>{ + dispatch(UPDATE_ANNOTATION_REQUEST) + return axios.put(APIROUTES.annotationById(annotationId), annotation) + .then(res => res.data) + .then(resData => dispatch({ + type: UPDATE_ANNOTATION_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: UPDATE_ANNOTATION_FAILURE, + payload: err + }))} + +export const deleteAnnotation = + annotationId => + (dispatch, getState) =>{ + dispatch(DELETE_ANNOTATION_REQUEST) + return axios.delete(APIROUTES.annotationById(annotationId), annotation) + .then(res => res.data) + .then(resData => dispatch({ + type: DELETE_ANNOTATION_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: DELETE_ANNOTATION_FAILURE, + payload: err + }))} diff --git a/browser/js/components/Annotator/index.js b/browser/js/components/Annotator/index.js index ee47be6..60eaea4 100644 --- a/browser/js/components/Annotator/index.js +++ b/browser/js/components/Annotator/index.js @@ -11,7 +11,6 @@ import {annotation, selection} from './actions'; class AnnotationHandler extends Component{ constructor(props){ super(props); - console.log(this.props); this.handleMouseUp = this.handleMouseUp.bind(this); this.handleMouseDown = this.handleMouseDown.bind(this); this.state = { @@ -49,10 +48,12 @@ class AnnotationHandler extends Component{ }); } componentWillReceiveProps(nextProps){ - console.log('receiving props'); this.setState({ selectionString: nextProps.selectionString }); + this.setState({ + currentLocation: nextProps.location + }) } render (){ return ( @@ -61,7 +62,10 @@ class AnnotationHandler extends Component{ {this.props.children}
- +
@@ -74,9 +78,20 @@ export class AnnotateContextMenu extends Component { constructor(props) { super(props); this.annotate = this.annotate.bind( this ); + this.state = { + currentLocation: props.currentLocation + } } annotate( ) { - this.props.dispatch(annotation(this.props.selection)); + let currentLocation = this.state.currentLocation + return this.props.dispatch(annotation({ + selection: this.props.selection, + annotation: this.props.selection, + location: currentLocation + })); + } + componentWillReceiveProps(nextProps){ + this.setState({currentLocation: nextProps.currentLocation}) } render() { return ( @@ -88,4 +103,12 @@ export class AnnotateContextMenu extends Component { } } -export default connect()(AnnotationHandler); +const mapStateToProps = (state, props)=>{ + let nextProps = {}; + // nextProps.currentLocation = state.currentFile.path; + // replace this \/ with this /\ + nextProps.currentLocation = 'https://github.com/Code-Genius/checkpoint-express-review/blob/master/app.js' + return nextProps; +} + +export default connect(mapStateToProps)(AnnotationHandler); diff --git a/browser/js/components/Annotator/reducer.js b/browser/js/components/Annotator/reducer.js index 8330e37..0f0e4a9 100644 --- a/browser/js/components/Annotator/reducer.js +++ b/browser/js/components/Annotator/reducer.js @@ -16,10 +16,17 @@ export default function annotationReducer(state = annotator_initialState, action selectionString: action.selectionString }) case 'ANNOTATION': + return Object.assign({}, state, action.payload, { + annotation: action.payload.annotation, + selection: action.payload.selection, + selectionString: action.payload.selectionString, + added: action.payload.added + }) + case 'ANNOTATION_CLEAR': + return Object.assign({}, state, action.payload) + case 'ANNOTATION_ADDED': return Object.assign({}, state, { - annotation: action.annotation, - selection: action.selection, - selectionString: action.selectionString + added: action.payload.added }) default: return state diff --git a/browser/js/components/Comment/apiActions.js b/browser/js/components/Comment/apiActions.js new file mode 100644 index 0000000..0aa2617 --- /dev/null +++ b/browser/js/components/Comment/apiActions.js @@ -0,0 +1,120 @@ +'use strict'; + +export const LOAD_COMMENTS_REQUEST = 'LOAD_COMMENTS_REQUEST' +export const LOAD_COMMENTS_SUCCESS = 'LOAD_COMMENTS_SUCCESS' +export const LOAD_COMMENTS_FAILURE = 'LOAD_COMMENTS_FAILURE' +export const CREATE_COMMENT_REQUEST = 'CREATE_COMMENT_REQUEST' +export const CREATE_COMMENT_SUCCESS = 'CREATE_COMMENT_SUCCESS' +export const CREATE_COMMENT_FAILURE = 'CREATE_COMMENT_FAILURE' +export const LOAD_COMMENT_REQUEST = 'LOAD_COMMENT_REQUEST' +export const LOAD_COMMENT_SUCCESS = 'LOAD_COMMENT_SUCCESS' +export const LOAD_COMMENT_FAILURE = 'LOAD_COMMENT_FAILURE' +export const UPDATE_COMMENT_REQUEST = 'UPDATE_COMMENT_REQUEST' +export const UPDATE_COMMENT_SUCCESS = 'UPDATE_COMMENT_SUCCESS' +export const UPDATE_COMMENT_FAILURE = 'UPDATE_COMMENT_FAILURE' + +export const getUserComments = + userId => + dispatch => { + dispatch(LOAD_COMMENTS_REQUEST) + return axios.get(APIROUTES.commentsByUserId(userId)) + }.then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_COMMENTS_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_COMMENTS_FAILURE, + payload: err + })) + +export const getCommentById = + commentId => + dispatch => { + dispatch(LOAD_COMMENT_REQUEST) + return axios.get(APIROUTES.commentById(commentId)) + }.then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_COMMENT_REQUEST, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_COMMENT_FAILURE, + payload: err + })) + +export const getCommentsByUserTest = + userTestId => + dispatch => { + dispatch(LOAD_COMMENTS_REQUEST) + return axios.get(APIROUTES.commentByUserTest(userTestId)) + }.then( res => res.data ) + .then( resData => dispatch({ + type: LOAD_COMMENTS_REQUEST, + payload: resData + })) + .catch( err => dispatch({ + type: LOAD_COMMENTS_FAILURE, + payload: err + })) + +export const postComment = + comment => + (dispatch, getState) =>{ + dispatch(CREATE_COMMENT_REQUEST) + return axios.post(APIROUTES.comment, comment) + }.then(res => res.data) + .then(resData => dispatch({ + type: CREATE_COMMENT_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: CREATE_COMMENT_FAILURE, + payload: err + })) + + +export const postCommentByUserTest = + (comment, userTestId) => + (dispath, getState) =>{ + dispatch(CREATE_COMMENT_REQUEST) + return axios.post(APIROUTES.commentByUserTest(userTestId), comment) + }.then( res => res.data) + .then(resData => dispatch({ + type: CREATE_COMMENT_SUCCESS + payload: resData + })) + .catch( err => dispatch({ + type: CREATE_COMMENT_FAILURE, + payloard: err + })) + +export const updateComment = + (comment, commentId) => + (dispatch, getState) =>{ + dispatch(UPDATE_COMMENT_REQUEST) + return axios.put(APIROUTES.commentById(commentId), comment) + }.then(res => res.data) + .then(resData => dispatch({ + type: UPDATE_COMMENT_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: UPDATE_COMMENT_FAILURE, + payload: err + })) + +export const deleteComment = + commentId => + (dispatch, getState) =>{ + dispatch(DELETE_COMMENT_REQUEST) + return axios.delete(APIROUTES.commentById(commentId), comment) + }.then(res => res.data) + .then(resData => dispatch({ + type: DELETE_COMMENT_SUCCESS, + payload: resData + })) + .catch( err => dispatch({ + type: DELETE_COMMENT_FAILURE, + payload: err + })) diff --git a/browser/js/components/Comment/index.js b/browser/js/components/Comment/index.js index 83cca8c..85c253a 100644 --- a/browser/js/components/Comment/index.js +++ b/browser/js/components/Comment/index.js @@ -23,6 +23,9 @@ import Chip from 'material-ui/Chip' import AddCircleOutline from 'material-ui/svg-icons/content/add-circle-outline'; import MarkdownWrapper from '../../containers/Markdown'; import {Tags} from '../../containers/Tag'; +import FlipMove from 'react-flip-move'; +import renderComment from './renderComment'; +import { annotationAdded } from '../Annotator/actions'; let criteria = ( @@ -49,12 +52,6 @@ let criteria = ( ) let defaultContents = { - // description: "foo bar", - // score: 1, - // solutionCodeLink: "http://www.google.com", - // tags: ['foo', 'bar', 'baz'], - // attachments: ['foo', 'bar', 'baz'], - // annotation: null, tags: [ {name: 'foo', color: '#3F51B5'} ], @@ -67,58 +64,22 @@ class Comment extends Component { super(props); this.buttonOnClickHandler = this.editMode.bind(this); this.onClickDoneHandler = this.editModeDone.bind(this); + this.state = { + contents: this.props.contents + } + this.renderComment = renderComment.bind(this) } componentWillReceiveProps(nextProps){ - if(nextProps.contents) { - let { contents } = nextProps; - this.contents = contents; + this.setState({isEditing: nextProps.isEditing}); + if(nextProps.contents.selection && this.state.isEditing && !nextProps.contents.selection.added) { + this.setState(function(previousState, currentProps){ + let nextState = {...previousState} + nextState.contents.selection = nextProps.contents.selection; + return nextState; + }) + this.props.dispatch(annotationAdded( true )); } } - // renderTextField(markdown, rendered, showMarkdown = false, editable=false){ - // return ( - //
- // {showMarkdown ? markdown : rendered} - //
- // ) - // } - renderComment () { - let contents = this.props.contents || defaultContents; - let isEditing = this.props.isEditing; - let buttonStyle = styles.assessmentButtons; - return (
- { (!contents.description && isEditing) ? : "" } - { (!contents.score && isEditing) ? : ""} - { (!contents.solutionCodeLink && isEditing) ? : ""} - { (!contents.tags && isEditing) ? : ( -
- -
- ) - } - { (!contents.attachments && isEditing) ? : ""} - { (!contents.selection) ? (isEditing ? : "") : ( -
-
- -
- < FlatButton href="/grade">Go to Code -
- ) } - { (!contents.criteria && isEditing) ? : contents.criteria } - { (!contents.markdown && isEditing) ? : ( -
- -
- ) } - -
) - - } editMode(){ this.props.dispatch({type: 'COMMENT_EDIT_START', payload: {key: this.props.commentIndex}}) } @@ -126,14 +87,6 @@ class Comment extends Component { this.props.dispatch({type: 'COMMENT_EDIT_DONE', payload: {key: null}}) } - // renderTags (tags, tagStyle) { - // if (tags) { - // return tags.map((tag, i) => { - // let thisTagStyle = _.cloneDeep(tagStyle); - // return {tag.name} - // }) - // } - // } render(){ const iconButtonElement = ( { - let nextProps = Object.assign( {}, props.contents); + let nextProps = {contents: {}}; + let stateToUpdate = Object.assign({}, state); - nextProps.isEditing = state.comment.isEditing.key === props.commentIndex ? true : false; + nextProps.isEditing = ( + state.comment.isEditing.key === props.commentIndex ? + true : false + ); + + nextProps.contents = Object.assign( {}, props.contents); + nextProps.contents.selection = props.contents.selection; - if(state.annotation.selectionString != ""){ - nextProps.selection = annotation.selection + if(!!state.annotation.selectionString && nextProps.isEditing && !stateToUpdate.annotation.added){ + nextProps.contents.selection = stateToUpdate.annotation.selection; } + return nextProps; } diff --git a/browser/js/components/Comment/renderComment.js b/browser/js/components/Comment/renderComment.js new file mode 100644 index 0000000..d3b22d8 --- /dev/null +++ b/browser/js/components/Comment/renderComment.js @@ -0,0 +1,67 @@ +'use strict'; +import React from 'react'; +import styles from '../graderStyles'; +import RaisedButton from 'material-ui/RaisedButton'; +import {Tags} from '../../containers/Tag'; +import MarkdownWrapper from '../../containers/Markdown'; +import FlatButton from 'material-ui/FlatButton' + + + +export default function renderComment () { + let contents = this.state.contents || defaultContents; + let isEditing = this.state.isEditing; + let buttonStyle = styles.assessmentButtons; + let id = 0; + return (
+ + { (!contents.description && isEditing) ? : "" } + + + { (!contents.score && isEditing) ? : ""} + + + { (!contents.solutionCodeLink && isEditing) ? : ""} + + + { (!contents.tags && isEditing) ? : ( +
+ +
+ ) + } +
+ + { (!contents.attachments && isEditing) ? : ""} + + + { (!contents.selection) ? (isEditing ? : "") : ( +
+
+
+              {contents.selection.toString()}
+            
+ {/**/} +
+ < FlatButton href="/grade">Go to Code +
+ ) } +
+ + { (!contents.criteria && isEditing) ? : contents.criteria } + + + { (!contents.markdown && isEditing) ? : ( +
+ +
+ ) } +
+
) + + } diff --git a/browser/js/components/Comment/test.js b/browser/js/components/Comment/test.js index ce62b3d..5f38547 100644 --- a/browser/js/components/Comment/test.js +++ b/browser/js/components/Comment/test.js @@ -28,7 +28,6 @@ import {annotation, selection, stopSelection, startSelection} from './actions'; export class AnnotationHandler extends Component{ constructor(props){ super(props); - console.log(this.props); this.handleMouseUp = this.handleMouseUp.bind(this); this.handleMouseDown = this.handleMouseDown.bind(this); this.state = { @@ -43,7 +42,6 @@ export class AnnotationHandler extends Component{ handleMouseUp (e){ let _selection = window.getSelection() let selectionString = _selection.toString(); - console.log('this is the selected string:', selectionString); if(_selection.type == 'Range'){ this.setState({ annotationStyles: { @@ -111,7 +109,6 @@ export class AnnotateContextMenu extends Component { export class TestAnnotate extends Component { constructor(props) { super(props); - console.log(this.props) } render() { return ( @@ -139,12 +136,10 @@ export class AnnotationWrapperTest extends Component { } function getStyle(state) { - console.log('called from getStyle', state); return state; } function getSelectionString(state) { - console.log('called get selection string'); return state } diff --git a/browser/js/components/GraderPanel/index.js b/browser/js/components/GraderPanel/index.js index e338cec..2df5fca 100644 --- a/browser/js/components/GraderPanel/index.js +++ b/browser/js/components/GraderPanel/index.js @@ -26,19 +26,9 @@ export default class GraderPanel extends Component { buildGraderPanel(this.props.dispatch); } - // renderCards (comments = SAMPLE_SPEC) { - // var self = this; - // return comments.map((contents, index) => { - // return ( - // - // - // ) - // }) - // } - render () { return ( -
+
@@ -63,7 +53,7 @@ export default class GraderPanel extends Component { } style={styles.skinny} diff --git a/browser/js/components/graderStyles.js b/browser/js/components/graderStyles.js index 350a825..1ab2855 100644 --- a/browser/js/components/graderStyles.js +++ b/browser/js/components/graderStyles.js @@ -3,39 +3,44 @@ import { blue600 } from 'material-ui/styles/colors' const styles = { + addBtn: { + marginLeft: 20, + backgroundColor: '#000', + borderRadius: '2px', + padding: 8, + height: 38, + width: 38 + }, + addTag: {width: '32px', height: '32px'}, + addTagIcon: {margin: '-8px -8px' }, assessmentButtons: { margin: 5 }, + assessmentInfo: { + padding: '20px', + backgroundColor: '#1E88E5' + }, blueBg: { backgroundColor: blue600 }, + center: { + textAlign: 'center' + }, content: { padding: 16 }, - skinny: { - margin: 0, - marginBottom: 15 - }, - noTopPadding: { - paddingTop: 0 - }, - infoCard: { - backgroundColor: '#1E88E5' - }, - inactiveCard: { - backgroundColor: '#aaa' + editAssessment: { + display: 'inline-block', + cursor: 'pointer' }, - roundedCard: { - borderRadius: 50 + formPaperStyle: { + margin: 'auto', + padding: 20 }, gradingInfo: { color: '#FFF', padding: 16 }, - assessmentInfo: { - padding: '20px', - backgroundColor: '#1E88E5' - }, gradingPane: { backgroundColor: '#64B5F6' }, @@ -48,31 +53,34 @@ const styles = { fontSize: 24, fontWeight: '400' }, + inactiveCard: { + backgroundColor: '#aaa' + }, + infoCard: { + backgroundColor: '#1E88E5' + }, + noTopPadding: { + paddingTop: 0 + }, paperStyle: { height: '100%', overflow: 'scroll' }, + roundedCard: { + borderRadius: 50 + }, + skinny: { + margin: 0, + marginBottom: 15 + }, + stepLabel: { + fontSize: 20 + }, student: { borderRadius: '50%', height: 40, marginRight: 10 }, - tag: { - margin: 4 - }, - tags: { - display: 'flex', - flexWrap: 'wrap', - }, - addTag: {width: '32px', height: '32px'}, - addTagIcon: {margin: '-8px -8px' }, - toggle: { - display: 'inline-table', - width: 'auto', - float: 'right', - top: 8, - position: 'relative' - }, studentIcon: { top: 7, marginLeft: 5, @@ -88,23 +96,19 @@ const styles = { background: 'white', borderRadius: '50%' }, - editAssessment: { - display: 'inline-block', - cursor: 'pointer' - }, - center: { - textAlign: 'center' + tag: { + margin: 4 }, - stepLabel: { - fontSize: 20 + tags: { + display: 'flex', + flexWrap: 'wrap', }, - addBtn: { - marginLeft: 20, - backgroundColor: '#000', - borderRadius: '2px', - padding: 8, - height: 38, - width: 38 + toggle: { + display: 'inline-table', + width: 'auto', + float: 'right', + top: 8, + position: 'relative' }, formPaperStyle: { margin: 'auto', diff --git a/browser/js/containers/App/index.js b/browser/js/containers/App/index.js index bb7e388..bf27dde 100644 --- a/browser/js/containers/App/index.js +++ b/browser/js/containers/App/index.js @@ -5,6 +5,7 @@ import getMuiTheme from 'material-ui/styles/getMuiTheme' import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' import { grey900, amber700 } from 'material-ui/styles/colors' import Navbar from '../../shared/Navbar' +import Socket from '../../io'; const muiTheme = getMuiTheme({ palette: { @@ -20,6 +21,7 @@ export default class App extends Component {
{this.props.children} +
) diff --git a/browser/js/containers/Grade/index.js b/browser/js/containers/Grade/index.js index 992165b..19b7f38 100644 --- a/browser/js/containers/Grade/index.js +++ b/browser/js/containers/Grade/index.js @@ -1,5 +1,7 @@ -import axios from "axios" +'use strict'; + import React, { Component, PropTypes } from 'react'; +import axios from "axios" import { PrismCode } from 'react-prism'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; diff --git a/browser/js/containers/Tag/index.js b/browser/js/containers/Tag/index.js index 9d764aa..1a3eaa1 100644 --- a/browser/js/containers/Tag/index.js +++ b/browser/js/containers/Tag/index.js @@ -3,27 +3,33 @@ import React, { Component, PropTypes } from 'react'; import IconButton from 'material-ui/IconButton'; import {grey400, darkBlack, lightBlack} from 'material-ui/styles/colors'; -import Chip from 'material-ui/Chip' +import Chip from 'material-ui/Chip'; import AddCircleOutline from 'material-ui/svg-icons/content/add-circle-outline'; import styles from '../../components/graderStyles'; import FlatButton from 'material-ui/FlatButton' import Dialog from 'material-ui/Dialog'; import RaisedButton from 'material-ui/RaisedButton'; import TextField from 'material-ui/TextField'; - - +import ReactStateAnimation from 'react-state-animation'; +import {cloneDeep as _cloneDeep} from 'lodash'; +import FlipMove from 'react-flip-move'; export class Tags extends Component{ constructor(props){ super(props); + let tagId = 0; this.state = { - tags: this.props.tags, isEditing: this.props.isEditing } + this.state.tags = this.props.tags.map( tag => { + tag.id = tagId++ + return tag; + }) this.addTag = this.addTag.bind(this); this.handleClose = this.handleClose.bind(this); this.submitNewTag = this.submitNewTag.bind(this); this.handleChange = this.handleChange.bind(this); + this.getTags = this.getTags.bind(this); } componentWillReceiveProps(nextProps){ this.state = { @@ -41,13 +47,17 @@ export class Tags extends Component{ handleChange(e){ this.setState({textAreaValue: e.target.value }) } - deleteTag(index, tag){ - console.log('deleting ', index) - let nextTags = this.state.tags.slice() - nextTags.splice(index.i, 1); - this.setState({tags: nextTags}); - // return ()=>alert(`deleting tag ${index.i} ${tag.tag.name}`) + deleteTag(index, tags){ + let currentTags = tags(); + const tagToDelete = + currentTags.map((tag) => {return tag.id}).indexOf(index); + let nextTags = currentTags.slice(); + nextTags.splice(tagToDelete, 1); + this.setState({tags: nextTags}) }; + getTags(){ + return this.state.tags; + } submitNewTag(){ let nextTags = this.state.tags.slice() nextTags.push({name: this.state.textAreaValue}) @@ -68,34 +78,87 @@ export class Tags extends Component{ onTouchTap={this.submitNewTag} /> ] + let that = this; return ( -
- {this.state.tags ? ( - this.state.tags.map((tag, i) => { - let thisTagStyle = _.cloneDeep(this.state.tagStyle); - return (this.deleteTag({i}, {tag})} backgroundColor={tag.color} >{tag.name}) - }) - ) : (null)} -
- { - this.state.isEditing ? ( - +
+ + {this.state.tags ? ( + this.state.tags.map((tag, i) => { + let thisTagStyle = _.cloneDeep(this.state.tagStyle); + return ( + + + ) + }) + ) : (null)} +
+ { + this.state.isEditing ? ( + - < AddCircleOutline color={grey400} hoverColor={lightBlack} style={ styles.addTagIcon } /> + < AddCircleOutline color={grey400} hoverColor={lightBlack} style={ styles.addTagIcon } /> - + - - ) : (null) - } + + ) : (null) + } +
+
-
+ ) + } +} + +class Tag extends Component{ + constructor(props){ + super(props) + let tagStyle = this.props.tagStyle; + tagStyle.opacity = 0; + this.state = { + tagName: this.props.tag.name, + opacity: 0, + backgroundColor: this.props.tag.color, + deleteTag: this.props.deleteTag + } + this._animate = new ReactStateAnimation(this); + } + + componentDidMount(){ + setTimeout(()=>{ + this._animate.cubicInOut( 'opacity', 1, 550 ) + }, + 225 * this.state.key + ) + } + fadeOut(){ + return this._animate.cubicInOut( 'opacity', 0, 550 ) + } + getStyle(){ + let style = this.props.tagStyle; + style.opacity = this.state.opacity; + return style; + } + render(){ + return( + this.state.deleteTag() } + > + {this.state.tagName} + ) } } diff --git a/browser/js/io/index.js b/browser/js/io/index.js new file mode 100644 index 0000000..d3203df --- /dev/null +++ b/browser/js/io/index.js @@ -0,0 +1,21 @@ +'use strict'; +import React, { Component } from 'react' +import io from 'socket.io-client' + +var client = io('http://localhost:1337'); + +export default class Socket extends Component{ + constructor(){ + super(); + } + render(){ + let that = this; + client.on('success', function(data){ + console.log('successful socket connection', this.id); + that.setState({socketId: this.id}) + }); + return( + + ) + } +} diff --git a/browser/js/utils/apiRoutes.js b/browser/js/utils/apiRoutes.js new file mode 100644 index 0000000..eb20908 --- /dev/null +++ b/browser/js/utils/apiRoutes.js @@ -0,0 +1,28 @@ +'use strict'; + +export const BASE = '/api' +export const VERSION = '/v1' +function api(routes){ + return `${BASE}${VERSION}${routes}` +} + +const APIROUTES = { + annotation: api('/annotations') +} + +export default APIROUTES; + +// export const ROUTES = new Map() +// .set('annotation', '/annotation') +// .set('annotations', '/annotations') +// .set('annotationsByUserId', (userId)=> `/annotations/${userId}`) +// .set('annotationById', (annotationId)=> `/annotation/${annotationId}`) +// .set('annotationsByUserTest', userTestId=> `/usertest/${userTestId}/annotations`) + +// +// const APIROUTES = (BASE, VERSION, ROUTES) => +// ROUTES +// .entries() +// .map( ([key, ROUTE]) => this[key] = `${BASE}${VERSION}${ROUTE}`, {} ); +// +// export default APIROUTES; diff --git a/package.json b/package.json index 2ca5bf0..a5d2b00 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "serve-favicon": "^2.2.0", "socket.io": "^1.3.4", "socket.io-client": "^1.3.5", - "webpack": "^1.13.1" + "webpack": "^1.13.1", + "xpath-dom": "^0.2.2" }, "devDependencies": { "babel-eslint": "^6.1.0", diff --git a/seed.js b/seed.js index 1289052..e82fa37 100644 --- a/seed.js +++ b/seed.js @@ -320,6 +320,7 @@ function addTagsToAssessments ( tags, numTags = 10, tagsPer = 10 ) { tagsToAdd.add(faker.random.arrayElement(tags)); } return Promise.all([...tagsToAdd.values()].map( tag =>{ + console.log(assessment); return assessment.addTag(tag) .catch(error => console.log(error) )} ) ) diff --git a/server/db/index.js b/server/db/index.js index 27bd6d2..3278f15 100644 --- a/server/db/index.js +++ b/server/db/index.js @@ -22,18 +22,9 @@ const Tag = require( './models/Tags/Tag'); const ItemTag = require( './models/Tags/ItemTag'); bluebird.all( [ User, Team, Organization, Annotation, CriterionResponse, Question, QuestionResponse, Rubric, StudentTest, Assessment, Tag ] ) - .then( Models => Models.forEach( Model => Model.addAssociations ? Model.addAssociations( db ) : null )) + .then( Models => + Models.forEach( Model => Model.addAssociations ? + Model.addAssociations( db ) : null ) + ) .then( () => console.log('done with associations')) .catch( error => console.log('association error', error)) - // .spread( ( User, Team, Organization, Annotation, CriterionResponse, Question, QuestionResponse, Rubric, StudentTest, Assessment, Tag, ItemTag ) => { - // User.addAssociations( db ); - // Team.addAssociations( db ); - // Organization.addAssociations( db ); - // Annotation.addAssociations( db ); - // CriterionResponse.addAssociations( db ); - // Question.addAssociations( db ); - // QuestionResponse.addAssociations( db ); - // Rubric.addAssociations( db ); - // StudentTest.addAssociations( db ); - // Assessment.addAssociations( db ); - // } ) diff --git a/server/db/models/Annotations/Annotation/definitions.js b/server/db/models/Annotations/Annotation/definitions.js index 1b3f9d1..452ea66 100644 --- a/server/db/models/Annotations/Annotation/definitions.js +++ b/server/db/models/Annotations/Annotation/definitions.js @@ -11,6 +11,27 @@ module.exports = function(db){ }, color: { type: Sequelize.STRING + }, + selectionString: { + type: Sequelize.TEXT + }, + anchorNode: { + type: Sequelize.STRING + }, + anchorOffset: { + type: Sequelize.STRING + }, + focusNode: { + type: Sequelize.STRING + }, + focusOffset: { + type: Sequelize.STRING + }, + location: { + type: Sequelize.STRING + }, + rangeCount: { + type: Sequelize.INTEGER } } } diff --git a/server/db/models/Annotations/Annotation/methods.js b/server/db/models/Annotations/Annotation/methods.js index 8a2f23a..2a22aa2 100644 --- a/server/db/models/Annotations/Annotation/methods.js +++ b/server/db/models/Annotations/Annotation/methods.js @@ -18,8 +18,8 @@ module.exports = { function addAssociations(db){ const Annotation = db.models['annotation']; const User = db.models['user']; - const Location = db.models['location']; + // const Location = db.models['location']; Annotation.belongsTo(User, {as: 'creator'}) - Annotation.belongsTo(Location) + // Annotation.belongsTo(Location) } diff --git a/server/db/models/Annotations/Location/methods.js b/server/db/models/Annotations/Location/methods.js index 3286181..c1f8adb 100644 --- a/server/db/models/Annotations/Location/methods.js +++ b/server/db/models/Annotations/Location/methods.js @@ -16,5 +16,5 @@ module.exports = { function addAssociations(){ const Annotation = db.models['annotation']; - Location.belongsTo(Annotation); + // Location.belongsTo(Annotation); } diff --git a/server/db/models/Tags/Tag/methods.js b/server/db/models/Tags/Tag/methods.js index 90d7083..faff9c9 100644 --- a/server/db/models/Tags/Tag/methods.js +++ b/server/db/models/Tags/Tag/methods.js @@ -17,23 +17,15 @@ module.exports = { function addAssociations( db ) { - console.log(Object.keys(db.models)); const Assessment = db.models['assessment']; const Tag = db.models[ 'tag' ]; const ItemTag = db.models['itemTag']; Assessment.belongsToMany(Tag, { through: {model: ItemTag, unique: false}, otherKey: 'tagId' - // unique: false - // foreignKey: 'taggable_id', - // constraints: false }); Tag.belongsToMany(Assessment, { through: {model: ItemTag, unique: false}, otherKey: 'assessmentId' - // unique: false - // foreignKey: 'tag_id' - // constraints: false }); - // console.log('describe itemTag', ItemTag.describe()); } diff --git a/server/io/index.js b/server/io/index.js index 9f78ff6..28d3519 100644 --- a/server/io/index.js +++ b/server/io/index.js @@ -8,10 +8,13 @@ module.exports = function (server) { io = socketio(server); - io.on('connection', function () { + io.on('connection', function (socket) { // Now have access to socket, wowzers! + console.log('socket connection'); + io.emit('success'); + socket.join(this.id); }); - + return io; }; diff --git a/webpack.config.js b/webpack.config.js index 17f5840..3e62590 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,6 +11,7 @@ module.exports = { path: __dirname + '/public', filename: 'main.js' }, + watch: true, resolve: { extensions: ['', '.js', '.jsx', '.scss', '.css'] },