import React from 'react'; import Link from './Link'; import Icon from './Icon'; import ArtistSentence from './ArtistSentence'; import Dater from './Dater'; import URILink from './URILink'; import ContextMenuTrigger from './ContextMenuTrigger'; import Popularity from './Popularity'; import ErrorBoundary from './ErrorBoundary'; import * as helpers from '../helpers'; export default class Track extends React.Component { constructor(props) { super(props); this.state = { hover: false, }; this.start_time = 0; this.end_time = 0; this.start_position = false; } handleMouseEnter(e) { this.setState({ hover: true }); } handleMouseLeave(e) { this.setState({ hover: false }); } handleMouseDown(e) { const target = $(e.target); // Clicked a nested link (ie Artist name), so no dragging required if (target.is('a')) { return false; } // Only listen for left mouse clicks if (e.button === 0) { this.start_position = { x: e.pageX, y: e.pageY, }; // Not left click, then ensure no dragging } else { this.start_position = false; } } handleMouseMove(e) { const target = $(e.target); // No drag handling means NO if (this.props.handleDrag === undefined) { return false; } if (this.start_position) { const start_x = this.start_position.x; const start_y = this.start_position.y; const threshold = 5; // Have we dragged outside of our threshold zone? if (e.pageX > start_x + threshold || e.pageX < start_x - threshold || e.pageY > start_y + threshold || e.pageY < start_y - threshold) { // Handover to parent for dragging. We can unset all our behaviour now. this.props.handleDrag(e); this.start_position = false; } } } handleMouseUp(e) { const target = $(e.target); // Only listen for left clicks if (e.button === 0) { if (this.props.dragger) { e.preventDefault(); if (this.props.handleDrop !== undefined) { this.props.handleDrop(e); } } else if (!target.is('a') && target.closest('a').length <= 0) { this.props.handleClick(e); this.start_position = false; } // Not left click, then ensure no dragging } else { this.start_position = false; return false; } } handleDoubleClick(e) { this.props.handleDoubleClick(e); } handleTouchStart(e) { const target = $(e.target); const timestamp = Math.floor(Date.now()); // Touch-drag zone if (target.hasClass('drag-zone')) { this.props.handleTouchDrag(e); e.preventDefault(); } // Save touch start details this.start_time = timestamp; this.start_position = { x: e.touches[0].clientX, y: e.touches[0].clientY, }; return false; } handleTouchEnd(e) { const target = $(e.target); const timestamp = Math.floor(Date.now()); const tap_distance_threshold = 10; // Max distance (px) between touchstart and touchend to qualify as a tap const tap_time_threshold = 200; // Max time (ms) between touchstart and touchend to qualify as a tap const end_position = { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY, }; // Too long between touchstart and touchend if (this.start_time + tap_time_threshold < timestamp) { return false; } // Make sure there's enough distance between start and end before we handle // this event as a 'tap' if (this.start_position.x + tap_distance_threshold > end_position.x && this.start_position.x - tap_distance_threshold < end_position.x && this.start_position.y + tap_distance_threshold > end_position.y && this.start_position.y - tap_distance_threshold < end_position.y) { // Clicked a nested link (ie Artist name), so no dragging required if (!target.is('a')) { e.preventDefault(); } // Context trigger if (target.hasClass('touch-contextable')) { // Update our selection. By not passing touch = true selection will work like a regular click // this.props.handleSelection(e); this.props.handleContextMenu(e); return false; } // We received a touchend within 300ms ago, so handle as double-tap if ((timestamp - this.end_time) > 0 && (timestamp - this.end_time) <= 300) { this.props.handleDoubleTap(e); e.preventDefault(); return false; } this.props.handleTap(e); } this.end_time = timestamp; } render() { if (!this.props.track) { return null; } const { track } = this.props; let className = 'list__item list__item--track mouse-draggable mouse-selectable mouse-contextable'; const track_details = []; const track_actions = []; if (track.artists) { track_details.push(