diff --git a/src/v2/components/RouteStatus/RouteStatus.js b/src/v2/components/RouteStatus/RouteStatus.js index 1903d535..e01eaceb 100644 --- a/src/v2/components/RouteStatus/RouteStatus.js +++ b/src/v2/components/RouteStatus/RouteStatus.js @@ -9,12 +9,7 @@ import getArrayFromObject from '@/v1/utils/getArrayFromObject'; import { StyleSheet, css } from '../../aphrodite'; -const RouteStatus = ({ - changeAscentResult, - onEditAdvancedClicked, - user, - -}) => ( +const RouteStatus = ({ onClick, user }) => ( { ({ route }) => { @@ -39,7 +34,7 @@ const RouteStatus = ({ role="button" tabIndex={0} style={{ outline: 'none' }} - onClick={changeAscentResult || null} + onClick={onClick || null} >
{ - event.stopPropagation(); - if (onEditAdvancedClicked) { - onEditAdvancedClicked(); - } - }} - > - ☰ -
- ) - } ); } @@ -115,13 +95,12 @@ const styles = StyleSheet.create({ }); RouteStatus.propTypes = { - changeAscentResult: PropTypes.func, - onEditAdvancedClicked: PropTypes.func, + user: PropTypes.object, + onClick: PropTypes.func, }; RouteStatus.defaultProps = { - changeAscentResult: null, - onEditAdvancedClicked: null, + onClick: null, }; const mapStateToProps = state => ({ diff --git a/src/v2/components/RoutesShowModal/RoutesShowModal.js b/src/v2/components/RoutesShowModal/RoutesShowModal.js index ae7446d2..6cbc6f7f 100644 --- a/src/v2/components/RoutesShowModal/RoutesShowModal.js +++ b/src/v2/components/RoutesShowModal/RoutesShowModal.js @@ -346,41 +346,7 @@ class RoutesShowModal extends Component { } }; - changeAscentResultV2 = () => this.props.history.push('#ascents'); - - changeAscentResult = (routeId) => { - const { - user, - routes, - addAscent, - updateAscent, - removeAscent, - } = this.props; - const route = routes[routeId]; - const ascent = R.find(R.propEq('user_id', user.id))(getArrayFromObject(route.ascents)); - if (ascent) { - if (ascent.history) { - this.changeAscentResultV2(); - return; - } - let result; - if (ascent.result === 'red_point') { - result = 'flash'; - } else if (ascent.result === 'flash') { - result = 'unsuccessful'; - removeAscent(ascent.id); - return; - } else { - result = 'red_point'; - } - const params = { ascent: { result } }; - updateAscent(ascent.id, params); - } else { - const result = 'red_point'; - const params = { ascent: { result, user_id: user.id, route_id: routeId } }; - addAscent(params); - } - }; + changeAscentResult = () => this.props.history.push('#ascents'); content = () => { const { @@ -647,10 +613,7 @@ class RoutesShowModal extends Component {
{ user && ( - this.changeAscentResult(routeId)} - onEditAdvancedClicked={this.changeAscentResultV2} - /> + ) }
diff --git a/src/v2/forms/RouteAscents/RouteAscents.jsx b/src/v2/forms/RouteAscents/RouteAscents.jsx index 69843aa4..389045a8 100644 --- a/src/v2/forms/RouteAscents/RouteAscents.jsx +++ b/src/v2/forms/RouteAscents/RouteAscents.jsx @@ -7,11 +7,13 @@ import * as R from 'ramda'; import Modal from '../../layouts/Modal'; import { currentUser } from '@/v2/redux/user_session/utils'; import RouteAscentsLayout from './RouteAscentsLayout'; -import { ApiUrl } from '@/v1/Environ'; import { updateAscent as updateAscentAction, addAscent as addAscentAction, -} from '@/v1/stores/routes/utils'; + removeAscent as removeAscentAction, +} from '@/v2/redux/routes/actions'; +import RouteAscentsTableContext from './contexts/RouteAscentsTableContext'; +import isHtmlElChild from '@/v2/utils/isHtmlElChild'; const SAVE_REQUEST_DELAY = 3000; @@ -23,34 +25,48 @@ class RouteAscents extends Component { this.state = { details: ascent, ascent, + mergeLastRow: true, }; this.timerId = null; + this.lastTableRowRef = null; } componentWillUnmount() { if (this.timerId) { clearTimeout(this.timerId); - this.save(); + this.save(true); } } - save = () => { - const { updateAscent } = this.props; + save = (removeIfEmpty) => { + const { removeAscent, updateAscent } = this.props; const { ascent } = this.state; const { id } = this.getAscent(); - updateAscent(`${ApiUrl}/v1/ascents/${id}`, { ascent }); + if (ascent.history === null && removeIfEmpty ) { + removeAscent(id); + } else { + updateAscent(id, { ascent }); + } }; - onAscentDateChanged = (id, date) => { - const { ascent } = this.state; + onAscentDateChanged = (index, date) => { + const { ascent, mergeLastRow } = this.state; const history = R.clone(ascent.history); - history[id].accomplished_at = date.format('YYYY-MM-DD'); + if (index !== history.length - 1 || (index === history.length - 1 && mergeLastRow)) { + let i = index - 1; + const fields = ['result', 'accomplished_at']; + while (i >= 0 && R.equals(R.pick(fields, history[i]), R.pick(fields, history[index]))) { + history[i].accomplished_at = date.format('YYYY-MM-DD'); + i -= 1; + } + } + history[index].accomplished_at = date.format('YYYY-MM-DD'); this.updateStateAscent(this.sortAscents(history)); }; - removeAscent = (id) => { + removeAscent = (index) => { const { ascent } = this.state; - const history = R.remove(id, 1, ascent.history); + const history = R.remove(index, 1, ascent.history); this.updateStateAscent(history); }; @@ -148,42 +164,44 @@ class RouteAscents extends Component { if (ascent) { const history = R.concat(ascent.history || [], this.prepareAscentsHistory(ascents)); this.updateStateAscent(history); + this.setState({ mergeLastRow: false }); } else { const { user, addAscent, history: historyProp } = this.props; let params; - if (ascents.length > 1) { - const lookup = { - red_point: 'success', - flash: 'success', - unsuccessful: 'attempt', - }; - const ascentsNew = R.map( - a => ( - { - result: lookup[a.result], - count: a.count, - } - ), + let ascentsPrepared = ascents; + if (ascents[0].result === 'red_point') { + ascentsPrepared = R.prepend( + { + ...ascents[0], + result: 'attempt', + count: 1, + }, ascents, ); - const history = this.prepareAscentsHistory(ascentsNew); - params = { - ascent: { - history, - result: this.getResult(history), - user_id: user.id, - route_id: this.getRouteId(), - }, - }; - } else { - params = { - ascent: { - result: ascents[0].result, - user_id: user.id, - route_id: this.getRouteId(), - }, - }; } + const lookup = { + red_point: 'success', + flash: 'success', + unsuccessful: 'attempt', + }; + const ascentsNew = R.map( + a => ( + { + result: lookup[a.result], + count: a.count, + } + ), + ascentsPrepared, + ); + const history = this.prepareAscentsHistory(ascentsNew); + params = { + ascent: { + history, + result: this.getResult(history), + user_id: user.id, + route_id: this.getRouteId(), + }, + }; addAscent(params); historyProp.goBack(); } @@ -193,25 +211,41 @@ class RouteAscents extends Component { } }; + onChangeFocus = (event) => { + const { mergeLastRow } = this.state; + if (!mergeLastRow) { + this.setState( + { mergeLastRow: !isHtmlElChild(event.target, this.lastTableRowRef) }, + ); + } + }; + render() { - const { details, ascent } = this.state; + const { details, ascent, mergeLastRow } = this.state; const ascentsHistory = this.ascentsForLayout(); return ( - + { this.lastTableRowRef = ref; } }} + > +
+ +
+
); } @@ -222,6 +256,7 @@ RouteAscents.propTypes = { routes: PropTypes.object.isRequired, addAscent: PropTypes.func.isRequired, updateAscent: PropTypes.func.isRequired, + removeAscent: PropTypes.func.isRequired, match: PropTypes.object.isRequired, history: PropTypes.object.isRequired, }; @@ -235,6 +270,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ addAscent: params => dispatch(addAscentAction(params)), updateAscent: (url, params) => dispatch(updateAscentAction(url, params)), + removeAscent: url => dispatch(removeAscentAction(url)), }); export default withRouter(connect(mapStateToProps, mapDispatchToProps)(RouteAscents)); diff --git a/src/v2/forms/RouteAscents/RouteAscentsLayout.jsx b/src/v2/forms/RouteAscents/RouteAscentsLayout.jsx index 0a07ba1f..0cb0b702 100644 --- a/src/v2/forms/RouteAscents/RouteAscentsLayout.jsx +++ b/src/v2/forms/RouteAscents/RouteAscentsLayout.jsx @@ -18,6 +18,7 @@ const RouteAscentsLayout = ({ onAscentDateChanged, ascents, instantMode, + mergeLastRow, }) => (
{title}
@@ -35,6 +36,7 @@ const RouteAscentsLayout = ({ 0}> { onAscentDateChanged && onAscentDateChanged(ascentId, newDate); diff --git a/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTable.jsx b/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTable.jsx index c4d80300..0cd514c9 100644 --- a/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTable.jsx +++ b/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTable.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import * as R from 'ramda'; import RouteAscentsTableLayout from './RouteAscentsTableLayout'; @@ -11,8 +12,50 @@ class RouteAscentsTable extends React.PureComponent { }; } + ascentsForLayout = () => { + const { ascents, mergeLastRow } = this.props; + const res = []; + R.forEach( + (ascent) => { + const fields = ['success', 'accomplished_at']; + if (res.length > 0 && R.equals(R.pick(fields, R.last(res)), R.pick(fields, ascent))) { + res[res.length - 1].count += 1; + res[res.length - 1].id = ascent.id; + } else { + res.push( + { + ...ascent, + count: 1, + id: ascent.id, + }, + ); + } + }, + ascents, + ); + + const last = R.last(res); + if (!mergeLastRow && last.count > 1) { + return R.concat( + R.slice(0, -1, res), + [ + { + ...last, + count: last.count - 1, + id: ascents[ascents.length - 2].id, + }, + { + ...last, + count: 1, + }, + ], + ); + } + return res; + }; + render() { - const { ascents, onRemoveAscent, onAscentDateChanged } = this.props; + const { onRemoveAscent, onAscentDateChanged } = this.props; return ( ); diff --git a/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTableLayout.jsx b/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTableLayout.jsx index a7bbde8b..439c298f 100644 --- a/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTableLayout.jsx +++ b/src/v2/forms/RouteAscents/RouteAscentsTable/RouteAscentsTableLayout.jsx @@ -2,6 +2,7 @@ import React from 'react'; import * as R from 'ramda'; import { StyleSheet, css } from '../../../aphrodite'; import Calendar from '../../../components/common/Calendar/Calendar'; +import RouteAscentsTableContext from '../contexts/RouteAscentsTableContext'; const RouteAscentsTableLayout = ({ @@ -11,76 +12,102 @@ const RouteAscentsTableLayout = ({ dateChangingAscentId, ascents, }) => ( - <> - - - - - - - - -
СтатусДата -
-
- - - { - ascents && R.addIndex(R.map)( - (ascent, i) => ( - - - - + + + ), + )(ascents) + } + +
{i + 1} - - { onDateClicked && onDateClicked(ascent.id); }} - > - {ascent.accomplished_at} - { - dateChangingAscentId === ascent.id && ( - { - onDateSelected && onDateSelected( - ascent.id, - newDate ? newDate : null, - ); - } + + { + ({ setLastRowRef }) => ( + <> + + + + + + + + +
СтатусДата + +
+
+ + + { + ascents && R.addIndex(R.map)( + (ascent, i) => ( + + + - + + - - ), - )(ascents) - } - -
{i + 1} + - ) - } - - { - onRemoveClicked && onRemoveClicked(ascent.id); + { + onDateClicked && onDateClicked(ascent.id); + }} + > + {ascent.accomplished_at} + { + dateChangingAscentId === ascent.id && ( + { + onDateSelected && onDateSelected( + ascent.id, + newDate ? newDate : null, + ); + } + } + /> + ) + } + + { + ascent.count > 1 && `×${ascent.count}` } - } - src={require('./assets/remove.svg')} - /> -
-
- +
+ { + onRemoveClicked && onRemoveClicked(ascent.id); + } + } + src={require('./assets/remove.svg')} + /> +
+
+ + ) + } + ); const style = StyleSheet.create({ diff --git a/src/v2/forms/RouteAscents/contexts/RouteAscentsTableContext.js b/src/v2/forms/RouteAscents/contexts/RouteAscentsTableContext.js new file mode 100644 index 00000000..676000b8 --- /dev/null +++ b/src/v2/forms/RouteAscents/contexts/RouteAscentsTableContext.js @@ -0,0 +1,5 @@ +import React from 'react'; + +const RouteAscentsTableContext = React.createContext(null); + +export default RouteAscentsTableContext; diff --git a/src/v2/utils/isHtmlElChild.js b/src/v2/utils/isHtmlElChild.js new file mode 100644 index 00000000..d6a1eb91 --- /dev/null +++ b/src/v2/utils/isHtmlElChild.js @@ -0,0 +1,7 @@ +import * as R from 'ramda'; + +const isHtmlElChild = (child, parent) => ( + R.contains(child, parent.getElementsByTagName('*')) +); + +export default isHtmlElChild;