mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-12 13:28:36 +03:00
correct timeline selection
This commit is contained in:
@@ -16,7 +16,7 @@ import Notification from './Notification.jsx'
|
||||
import StaticPage from './StaticPage'
|
||||
import TemplateCover from './TemplateCover'
|
||||
|
||||
import { parseDate } from '../common/utilities'
|
||||
import { parseDate, binarySearch } from '../common/utilities'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
@@ -29,8 +29,6 @@ class Dashboard extends React.Component {
|
||||
this.moveInNarrative = this.moveInNarrative.bind(this)
|
||||
this.handleSelect = this.handleSelect.bind(this)
|
||||
this.getCategoryColor = this.getCategoryColor.bind(this)
|
||||
|
||||
this.eventsById = {}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -49,23 +47,36 @@ class Dashboard extends React.Component {
|
||||
this.props.actions.updateHighlighted((highlighted) || null)
|
||||
}
|
||||
|
||||
getEventById (eventId) {
|
||||
if (this.eventsById[eventId]) return this.eventsById[eventId]
|
||||
this.eventsById[eventId] = this.props.domain.events.find(ev => ev.id === eventId)
|
||||
return this.eventsById[eventId]
|
||||
}
|
||||
|
||||
handleViewSource (source) {
|
||||
this.props.actions.updateSource(source)
|
||||
}
|
||||
|
||||
handleSelect (selected) {
|
||||
if (selected) {
|
||||
let eventsToSelect = selected.map(event => this.getEventById(event.id))
|
||||
eventsToSelect = eventsToSelect.sort((a, b) => parseDate(a.timestamp) - parseDate(b.timestamp))
|
||||
|
||||
this.props.actions.updateSelected(eventsToSelect)
|
||||
handleSelect (selected, axis) {
|
||||
const matchedEvents = [selected]
|
||||
const TIMELINE_AXIS = 0
|
||||
if (axis === TIMELINE_AXIS) {
|
||||
// find in events
|
||||
const { events } = this.props.domain
|
||||
const idx = binarySearch(
|
||||
events,
|
||||
selected,
|
||||
(e1, e2) => new Date(e1.timestamp) - new Date(e2.timestamp)
|
||||
)
|
||||
// check events before
|
||||
let ptr = idx - 1
|
||||
while (events[idx].timestamp === events[ptr].timestamp) {
|
||||
matchedEvents.push(events[ptr])
|
||||
ptr -= 1
|
||||
}
|
||||
// check events after
|
||||
ptr = idx + 1
|
||||
while (events[idx].timestamp === events[ptr].timestamp) {
|
||||
matchedEvents.push(events[ptr])
|
||||
ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
this.props.actions.updateSelected(matchedEvents)
|
||||
}
|
||||
|
||||
getCategoryColor (category) {
|
||||
|
||||
@@ -272,7 +272,7 @@ function mapStateToProps (state) {
|
||||
},
|
||||
app: {
|
||||
views: state.app.filters.views,
|
||||
selected: state.app.selected,
|
||||
selected: selectors.selectSelected(state),
|
||||
highlighted: state.app.highlighted,
|
||||
map: state.app.map,
|
||||
narrative: state.app.narrative,
|
||||
|
||||
@@ -14,6 +14,7 @@ import ZoomControls from './presentational/Timeline/ZoomControls.js'
|
||||
import Markers from './presentational/Timeline/Markers.js'
|
||||
import Events from './presentational/Timeline/Events.js'
|
||||
import Categories from './TimelineCategories.jsx'
|
||||
const TIMELINE_AXIS = 0
|
||||
|
||||
class Timeline extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -336,14 +337,14 @@ class Timeline extends React.Component {
|
||||
dims={dims}
|
||||
selected={this.props.app.selected}
|
||||
getEventX={this.getDatetimeX}
|
||||
getY={e => this.state.scaleY(e.category)}
|
||||
getCategoryY={this.state.scaleY}
|
||||
transitionDuration={this.state.transitionDuration}
|
||||
styles={this.props.ui.styles}
|
||||
noCategories={this.props.domain.categories && this.props.domain.categories.length}
|
||||
features={this.props.features}
|
||||
/>
|
||||
<Events
|
||||
events={this.props.domain.eventsAndProjects[0]}
|
||||
projects={this.props.domain.eventsAndProjects[1]}
|
||||
events={this.props.domain.events}
|
||||
projects={this.props.domain.projects}
|
||||
styleDatetime={this.styleDatetime}
|
||||
narrative={this.props.app.narrative}
|
||||
getDatetimeX={this.getDatetimeX}
|
||||
@@ -356,7 +357,7 @@ class Timeline extends React.Component {
|
||||
}}
|
||||
getCategoryColor={this.props.methods.getCategoryColor}
|
||||
transitionDuration={this.state.transitionDuration}
|
||||
onSelect={this.props.methods.onSelect}
|
||||
onSelect={ev => this.props.methods.onSelect(ev, TIMELINE_AXIS)}
|
||||
dims={dims}
|
||||
features={this.props.features}
|
||||
/>
|
||||
@@ -373,7 +374,8 @@ function mapStateToProps (state) {
|
||||
dimensions: selectors.selectDimensions(state),
|
||||
isNarrative: !!state.app.narrative,
|
||||
domain: {
|
||||
eventsAndProjects: selectors.selectEventsAndProjects(state),
|
||||
events: selectors.selectStackedEvents(state),
|
||||
projects: selectors.selectProjects(state),
|
||||
categories: selectors.getCategories(state),
|
||||
narratives: state.domain.narratives
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ function renderDot (event, styles, props) {
|
||||
|
||||
function renderBar (event, styles, props) {
|
||||
const fillOpacity = props.features.GRAPH_NONLOCATED
|
||||
? event.projectOffset >= 0 ? styles.opacity : 0.05
|
||||
? event.projectOffset >= 0 ? styles.opacity : 0.5
|
||||
: 0.6
|
||||
|
||||
return <DatetimeBar
|
||||
@@ -80,7 +80,8 @@ const TimelineEvents = ({
|
||||
}
|
||||
}
|
||||
|
||||
let renderShape = renderDot
|
||||
const isDot = (!!event.location && !!event.longitude) || (features.GRAPH_NONLOCATED && event.projectOffset !== -1)
|
||||
let renderShape = isDot ? renderDot : renderBar
|
||||
if (event.shape) {
|
||||
if (event.shape === 'bar') {
|
||||
renderShape = renderBar
|
||||
@@ -103,7 +104,7 @@ const TimelineEvents = ({
|
||||
y: (features.GRAPH_NONLOCATED && !event.latitude && !event.longitude)
|
||||
? event.projectOffset >= 0 ? dims.trackHeight - event.projectOffset : dims.marginTop
|
||||
: getCategoryY ? getCategoryY(event.category) : () => null,
|
||||
onSelect: () => onSelect([event]),
|
||||
onSelect: () => onSelect(event),
|
||||
dims,
|
||||
highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.tags[features.HIGHLIGHT_GROUPS.tagIndexIndicatingGroup]) : [],
|
||||
features
|
||||
|
||||
@@ -4,14 +4,18 @@ import colors, { sizes } from '../../../common/global'
|
||||
const TimelineMarkers = ({
|
||||
styles,
|
||||
getEventX,
|
||||
getY,
|
||||
getCategoryY,
|
||||
transitionDuration,
|
||||
selected,
|
||||
dims,
|
||||
noCategories
|
||||
features
|
||||
}) => {
|
||||
function renderMarker (event) {
|
||||
function renderCircle () {
|
||||
const yVal = (features.GRAPH_NONLOCATED && !event.latitude && !event.longitude)
|
||||
? event.projectOffset >= 0 ? dims.trackHeight - event.projectOffset : dims.marginTop
|
||||
: getCategoryY ? getCategoryY(event.category) : () => null
|
||||
|
||||
return <circle
|
||||
className='timeline-marker'
|
||||
cx={0}
|
||||
@@ -22,7 +26,7 @@ const TimelineMarkers = ({
|
||||
stroke-linejoin='round'
|
||||
stroke-dasharray={styles ? styles['stroke-dasharray'] : '2,2'}
|
||||
style={{
|
||||
'transform': `translate(${getEventX(event.timestamp)}px, ${getY(event)}px)`,
|
||||
'transform': `translate(${getEventX(event.timestamp)}px, ${yVal}px)`,
|
||||
'-webkit-transition': `transform ${transitionDuration / 1000}s ease`,
|
||||
'-moz-transition': 'none',
|
||||
'opacity': 0.9
|
||||
@@ -47,7 +51,7 @@ const TimelineMarkers = ({
|
||||
}}
|
||||
/>
|
||||
}
|
||||
const isLocated = !!event.latitude && !!event.longitude
|
||||
const isDot = (!features.GRAPH_NONLOCATED && !!event.latitude && !!event.longitude) || (features.GRAPH_NONLOCATED && (event.projectOffset !== -1 || (!!event.latitude && !!event.longitude)))
|
||||
switch (event.shape) {
|
||||
case 'circle':
|
||||
return renderCircle()
|
||||
@@ -58,7 +62,7 @@ const TimelineMarkers = ({
|
||||
case 'star':
|
||||
return renderCircle()
|
||||
default:
|
||||
return isLocated ? renderBar() : renderCircle()
|
||||
return isDot ? renderCircle() : renderBar()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user