diff --git a/src/actions/drawing.js b/src/actions/drawing.js index cd358b4f..afc2a775 100644 --- a/src/actions/drawing.js +++ b/src/actions/drawing.js @@ -33,6 +33,10 @@ export const clearShapes = () => ({ type: ActionConstants.CLEAR_SHAPES }) +export const clearShapesInProgress = () => ({ + type: ActionConstants.CLEAR_SHAPES_IN_PROGRESS +}) + export const saveEdits = () => ({ type: ActionConstants.SAVE_EDITS }) diff --git a/src/components/Markings/DrawingClassifier.js b/src/components/Markings/DrawingClassifier.js index 4ea58a7e..206777e9 100644 --- a/src/components/Markings/DrawingClassifier.js +++ b/src/components/Markings/DrawingClassifier.js @@ -170,6 +170,7 @@ class DrawingClassifier extends Component { uri={this.state.localImagePath} onImageLayout={this.onImageLayout} showBlurView={R.isEmpty(this.props.shapes)} + alreadySeen={this.props.subject.already_seen} subjectDimensions={this.props.subjectDimensions} displayToNativeRatio={this.props.subjectDimensions.naturalWidth/this.state.subjectDimensions.clientWidth} /> diff --git a/src/components/Markings/DrawingModal.js b/src/components/Markings/DrawingModal.js index 2bdf3ad7..fd3faed6 100644 --- a/src/components/Markings/DrawingModal.js +++ b/src/components/Markings/DrawingModal.js @@ -53,16 +53,20 @@ class DrawingModal extends Component { } } - onCancel() { + onCancel({justClearInProgress}) { const onConfirm = () => { - this.props.drawingActions.clearShapes() + if (justClearInProgress) { + this.props.drawingActions.clearShapesInProgress() + } else { + this.props.drawingActions.clearShapes() + } this.props.onClose() } if (this.props.shouldConfirmOnClose) { Alert.alert( 'Are you sure?', - 'This will erase all of your annotations.', + `This will erase ${justClearInProgress ? 'your most recent edits' : 'all of your annotations.'}`, [ {text: 'Yes', onPress: onConfirm}, {text: 'Cancel', style: 'cancel'}, @@ -109,13 +113,13 @@ class DrawingModal extends Component { this.onCancel({justClearInProgress: true})} onSave={this.onSave} warnForRequirements={this.props.warnForRequirements && this.props.numberOfShapesDrawn < this.props.tool.min} /> this.onCancel({justClearInProgress: false})} style={styles.closeButton} color={Theme.$zooniverseTeal} backgroundColor="white" @@ -179,7 +183,8 @@ DrawingModal.propTypes = { drawingActions: PropTypes.shape({ saveEdits: PropTypes.func, clearShapes: PropTypes.func, - undoMostRecentEdit: PropTypes.func + undoMostRecentEdit: PropTypes.func, + clearShapesInProgress: PropTypes.func }), warnForRequirements: PropTypes.bool, numberOfShapesDrawn: PropTypes.number diff --git a/src/components/Markings/ImageWithSvgOverlay.js b/src/components/Markings/ImageWithSvgOverlay.js index 8edbb55a..1b439b41 100644 --- a/src/components/Markings/ImageWithSvgOverlay.js +++ b/src/components/Markings/ImageWithSvgOverlay.js @@ -14,6 +14,7 @@ import R from 'ramda' import { BlurView } from 'react-native-blur'; import Icon from 'react-native-vector-icons/FontAwesome' import SubjectLoadingIndicator from '../common/SubjectLoadingIndicator'; +import AlreadySeenBanner from '../classifier/AlreadySeenBanner' class ImageWithSvgOverlay extends Component { @@ -139,7 +140,8 @@ class ImageWithSvgOverlay extends Component { : } - { this.props.showBlurView && this.props.imageIsLoaded && this.renderBlurView()} + { this.props.showBlurView && this.props.imageIsLoaded && this.renderBlurView() } + { this.props.alreadySeen && this.props.imageIsLoaded && } ) } @@ -203,7 +205,8 @@ ImageWithSvgOverlay.propTypes = { })), onImageLayout: PropTypes.func, shapes: PropTypes.object, - showBlurView: PropTypes.bool + showBlurView: PropTypes.bool, + alreadySeen: PropTypes.bool } export default ImageWithSvgOverlay \ No newline at end of file diff --git a/src/components/Markings/components/ShapeEditorSvg.js b/src/components/Markings/components/ShapeEditorSvg.js index 193c090f..1d3b748d 100644 --- a/src/components/Markings/components/ShapeEditorSvg.js +++ b/src/components/Markings/components/ShapeEditorSvg.js @@ -100,7 +100,6 @@ class ShapeEditorSvg extends Component { const { shapeIndex, touchState, shapeRefs } = this.state if (shapeIndex >= 0) { const { dx, dy } = gestureState; - // Because Svgs don't have any way to animate, we have to update their props manually const deltas = calculateShapeChanges(touchState, dx, dy, this.props.displayToNativeRatioX, this.props.displayToNativeRatioY) shapeRefs[shapeIndex].update(deltas); @@ -165,9 +164,14 @@ class ShapeEditorSvg extends Component { displayToNativeRatioY={this.props.displayToNativeRatioY} showCorners={this.props.mode === 'edit' && selectedShape} isDeletable={this.props.mode === 'erase'} - ref={ref => this.setState({ - shapeRefs: R.set(R.lensProp(index), ref ,this.state.shapeRefs) - })} + ref={ref => { + if (ref) { + this.setState(currentState => { + currentState.shapeRefs = R.set(R.lensProp(index), ref, currentState.shapeRefs) + return currentState + }) + } + }} /> ) } diff --git a/src/components/classifier/AlreadySeenBanner.js b/src/components/classifier/AlreadySeenBanner.js new file mode 100644 index 00000000..c733fc19 --- /dev/null +++ b/src/components/classifier/AlreadySeenBanner.js @@ -0,0 +1,36 @@ +import React from 'react' +import { + View +} from 'react-native' +import FontedText from '../common/FontedText' +import EStyleSheet from 'react-native-extended-stylesheet' + +const AlreadySeenBanner = () => { + return ( + + + ALREADY SEEN! + + + ) +} + +const styles = EStyleSheet.create({ + alreadySeen: { + elevation: 2, + position: 'absolute', + top: 16, + right: 0, + backgroundColor: '$darkOrange', + paddingVertical: 2, + paddingHorizontal: 5, + transform: [{ rotate: '20deg'}] + }, + alreadySeenText: { + color: 'white', + fontSize: 12, + fontWeight: 'bold' + }, +}) + +export default AlreadySeenBanner \ No newline at end of file diff --git a/src/components/classifier/SwipeCard.js b/src/components/classifier/SwipeCard.js index cd69bbe5..f50c04b1 100644 --- a/src/components/classifier/SwipeCard.js +++ b/src/components/classifier/SwipeCard.js @@ -17,6 +17,7 @@ import * as imageActions from '../../actions/images' import { subjectDisplayWidth, subjectDisplayHeight } from './SwipeClassifier' import ProgressIndicatingImage from '../common/ProgressIndicatingImage' import FontedText from '../common/FontedText' +import AlreadySeenBanner from './AlreadySeenBanner' const mapDispatchToProps = (dispatch) => ({ imageActions: bindActionCreators(imageActions, dispatch) @@ -93,13 +94,6 @@ class SwipeCard extends Component { } const alreadySeen = (this.props.subject.already_seen || this.props.seenThisSession) && this.state.imageIsVisible - const alreadySeenBanner = - - - ALREADY SEEN! - - - const overlay = @@ -126,7 +120,7 @@ class SwipeCard extends Component { { this.props.shouldAnimateOverlay ? overlay : null } - { alreadySeen ? alreadySeenBanner : null } + { alreadySeen ? : null } @@ -137,21 +131,6 @@ class SwipeCard extends Component { export default connect(null, mapDispatchToProps)(SwipeCard) const styles = EStyleSheet.create({ - alreadySeen: { - elevation: 2, - position: 'absolute', - top: 16, - right: 0, - backgroundColor: '$darkOrange', - paddingVertical: 2, - paddingHorizontal: 5, - transform: [{ rotate: '20deg'}] - }, - alreadySeenText: { - color: 'white', - fontSize: 12, - fontWeight: 'bold' - }, imageShadow: { backgroundColor: 'transparent', shadowColor: 'rgba(0, 0, 0, 0.24)', diff --git a/src/constants/actions.js b/src/constants/actions.js index f5c45adf..0f74ccbf 100644 --- a/src/constants/actions.js +++ b/src/constants/actions.js @@ -66,3 +66,4 @@ export const CLEAR_SHAPES = 'CLEAR_SHAPES' export const SAVE_EDITS = 'SAVE_EDITS' export const MUTATE_SHAPE = 'MUTATE_SHAPE' export const UNDO_EDIT = 'UNDO_EDIT' +export const CLEAR_SHAPES_IN_PROGRESS = 'CLEAR_SHAPES_IN_PROGRESS' diff --git a/src/reducers/classifierReducer.js b/src/reducers/classifierReducer.js index 539c4cba..70346ffb 100644 --- a/src/reducers/classifierReducer.js +++ b/src/reducers/classifierReducer.js @@ -27,10 +27,7 @@ export default function classifier(state=InitialClassifier, action) { case ActionConstants.APPEND_SUBJECTS_TO_WORKFLOW: { const subjectList = state.subjectLists[action.workflowId] || [] const workflowIdLens = R.lensProp(action.workflowId) - const filteredNewSubjects = action.subjects.filter((newSubject) => { - return !R.any(subject => subject.id === newSubject.id, subjectList) - }) - const newSubjectList = R.set(workflowIdLens, subjectList.concat(filteredNewSubjects), state.subjectLists) + const newSubjectList = R.set(workflowIdLens, [...subjectList, ...action.subjects], state.subjectLists) return { ...state, subjectLists: newSubjectList} } case ActionConstants.CLEAR_SUBJECTS_FROM_WORKFLOW: { @@ -72,7 +69,16 @@ export default function classifier(state=InitialClassifier, action) { const workflowIdLens = R.lensProp(action.workflowId) const subjectsSeenArray = state.seenThisSession[action.workflowId] || [] subjectsSeenArray.push(action.subjectId) - return { ...state, seenThisSession: R.set(workflowIdLens, subjectsSeenArray, state.seenThisSession)} + const updatedSubjects = state.subjectLists[action.workflowId].map(subject => { + if (subject.id === action.subjectId) { + return R.set(R.lensProp('already_seen'), true, subject) + } else { + return subject + } + }) + const seenThisSession = R.set(workflowIdLens, subjectsSeenArray, state.seenThisSession) + const subjectLists = R.set(workflowIdLens, updatedSubjects, state.subjectLists) + return { ...state, seenThisSession, subjectLists} } case ActionConstants.SET_QUESTION_CONTAINER_HEIGHT: { const workflowIdLens = R.lensProp(action.workflowId) diff --git a/src/reducers/drawingReducer.js b/src/reducers/drawingReducer.js index cec238b8..fbc8ec97 100644 --- a/src/reducers/drawingReducer.js +++ b/src/reducers/drawingReducer.js @@ -73,7 +73,6 @@ export default function drawing(state=InitialState, action) { return { ...state, shapesInProgress: {}, actions: [], shapes: {} } } case (ActionConstants.SAVE_EDITS): { - return { ...state, shapes: state.shapesInProgress, actions: [] } } case (ActionConstants.MUTATE_SHAPE): { @@ -88,6 +87,9 @@ export default function drawing(state=InitialState, action) { const shapesInProgress = calculateChanges(state.shapes, actionsAfterUndo) return {...state, actions: actionsAfterUndo, shapesInProgress} } + case (ActionConstants.CLEAR_SHAPES_IN_PROGRESS): { + return {...state, actions: [], shapesInProgress: state.shapes} + } default: return state }