Skip to content

Commit

Permalink
Merge pull request #163 from forensic-architecture/feature/refactor-c…
Browse files Browse the repository at this point in the history
…ategories

Feature/refactor categories
  • Loading branch information
ebefarooqui authored Oct 20, 2020
2 parents 077ad59 + 54819f5 commit 488a5d0
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 78 deletions.
28 changes: 14 additions & 14 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { urlFromEnv } from '../common/utilities'
// TODO: relegate these URLs entirely to environment variables
// const CONFIG_URL = urlFromEnv('CONFIG_EXT')
const EVENT_DATA_URL = urlFromEnv('EVENTS_EXT')
const CATEGORY_URL = urlFromEnv('CATEGORIES_EXT')
// const CATEGORY_URL = urlFromEnv('CATEGORIES_EXT')
const ASSOCIATIONS_URL = urlFromEnv('ASSOCIATIONS_EXT')
const SOURCES_URL = urlFromEnv('SOURCES_EXT')
const SITES_URL = urlFromEnv('SITES_EXT')
Expand Down Expand Up @@ -42,13 +42,6 @@ export function fetchDomain () {
)
).then(results => results.flatMap(t => t))

let catPromise = Promise.resolve([])
if (features.USE_CATEGORIES) {
catPromise = fetch(CATEGORY_URL)
.then(response => response.json())
.catch(() => handleError(domainMsg('categories')))
}

let associationsPromise = Promise.resolve([])
if (features.USE_ASSOCIATIONS) {
if (!ASSOCIATIONS_URL) {
Expand Down Expand Up @@ -87,7 +80,6 @@ export function fetchDomain () {

return Promise.all([
eventPromise,
catPromise,
associationsPromise,
sourcesPromise,
sitesPromise,
Expand All @@ -96,17 +88,17 @@ export function fetchDomain () {
.then(response => {
const result = {
events: response[0],
categories: response[1],
associations: response[2],
sources: response[3],
sites: response[4],
shapes: response[5],
associations: response[1],
sources: response[2],
sites: response[3],
shapes: response[4],
notifications
}
if (Object.values(result).some(resp => resp.hasOwnProperty('error'))) {
throw new Error('Some URLs returned negative. If you are in development, check the server is running')
}
dispatch(toggleFetchingDomain())
dispatch(setInitialCategories(result.associations))
return result
})
.catch(err => {
Expand Down Expand Up @@ -212,6 +204,14 @@ export function setNotLoading () {
}
}

export const SET_INITIAL_CATEGORIES = 'SET_INITIAL_CATEGORIES'
export function setInitialCategories (values) {
return {
type: SET_INITIAL_CATEGORIES,
values
}
}

export const UPDATE_TIMERANGE = 'UPDATE_TIMERANGE'
export function updateTimeRange (timerange) {
return {
Expand Down
7 changes: 5 additions & 2 deletions src/common/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export const FILTER_MODE = 'FILTER'
export const NARRATIVE_MODE = 'NARRATIVE'
export const ASSOCIATION_MODES = {
CATEGORY: 'CATEGORY',
NARRATIVE: 'NARRATIVE',
FILTER: 'FILTER'
}
12 changes: 12 additions & 0 deletions src/common/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ export function trimAndEllipse (string, stringNum) {
return string
}

export function getEventCategories (event, categories) {
const matchedCategories = []
if (event.associations && event.associations.length > 0) {
event.associations.reduce((acc, val) => {
const foundCategory = categories.find(cat => cat.id === val)
if (foundCategory) acc.push(foundCategory)
return acc
}, matchedCategories)
}
return matchedCategories
}

/**
* Inset the full source represenation from 'allSources' into an event. The
* function is 'curried' to allow easy use with maps. To use for a single
Expand Down
18 changes: 13 additions & 5 deletions src/components/Timeline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Timeline extends React.Component {
makeScaleY (categories, trackHeight, marginTop) {
const { features } = this.props
if (features.GRAPH_NONLOCATED && features.GRAPH_NONLOCATED.categories) {
categories = categories.filter(cat => !features.GRAPH_NONLOCATED.categories.includes(cat.category))
categories = categories.filter(cat => !features.GRAPH_NONLOCATED.categories.includes(cat.id))
}
const catHeight = trackHeight / (categories.length)
const shiftUp = trackHeight / (categories.length) / 2
Expand All @@ -87,7 +87,8 @@ class Timeline extends React.Component {
const catsYpos = categories.map((g, i) => {
return ((i + 1) * catHeight) - shiftUp + marginShift + manualAdjustment
})
const catMap = categories.map(c => c.category)
const catMap = categories.map(c => c.id)

return (cat) => {
const idx = catMap.indexOf(cat)
return catsYpos[idx]
Expand Down Expand Up @@ -268,11 +269,16 @@ class Timeline extends React.Component {

getY (event) {
const { features, domain } = this.props
const { USE_CATEGORIES, GRAPH_NONLOCATED } = features
const { categories } = domain

const categoriesExist = categories && categories.length > 0

const { GRAPH_NONLOCATED } = features

if (!USE_CATEGORIES) { return this.state.dims.trackHeight / 2 }
if (!categoriesExist) { return this.state.dims.trackHeight / 2 }

const { category, project } = event

if (GRAPH_NONLOCATED && GRAPH_NONLOCATED.categories.includes(category)) {
return this.state.dims.marginTop + domain.projects[project].offset + this.props.ui.eventRadius
}
Expand Down Expand Up @@ -336,7 +342,7 @@ class Timeline extends React.Component {
onDragStart={() => { this.onDragStart() }}
onDrag={() => { this.onDrag() }}
onDragEnd={() => { this.onDragEnd() }}
categories={this.props.domain.categories}
categories={this.props.app.activeCategories}
features={this.props.features}
/>
<Handles
Expand All @@ -362,6 +368,7 @@ class Timeline extends React.Component {
<Events
events={this.props.domain.events}
projects={this.props.domain.projects}
categories={this.props.domain.categories}
styleDatetime={this.styleDatetime}
narrative={this.props.app.narrative}
getDatetimeX={this.getDatetimeX}
Expand Down Expand Up @@ -400,6 +407,7 @@ function mapStateToProps (state) {
narratives: state.domain.narratives
},
app: {
activeCategories: selectors.getActiveCategories(state),
selected: state.app.selected,
language: state.app.language,
timeline: state.app.timeline,
Expand Down
16 changes: 8 additions & 8 deletions src/components/TimelineCategories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ class TimelineCategories extends React.Component {

renderCategory (cat, idx) {
const { features, dims } = this.props
const { category } = cat
const strokeWidth = 1 // dims.trackHeight / (this.props.categories.length + 1)
if (features.GRAPH_NONLOCATED &&
features.GRAPH_NONLOCATED.categories &&
features.GRAPH_NONLOCATED.categories.includes(category)) {
features.GRAPH_NONLOCATED.categories.includes(cat)) {
return null
}

Expand All @@ -40,26 +39,27 @@ class TimelineCategories extends React.Component {
class='tick'
style={{ strokeWidth }}
opacity='0.5'
transform={`translate(0,${this.props.getCategoryY(category)})`}
transform={`translate(0,${this.props.getCategoryY(cat)})`}
>
<line x1={dims.marginLeft} x2={dims.width - dims.width_controls} />
</g>
<g class='tick' opacity='1' transform={`translate(0,${this.props.getCategoryY(category)})`}>
<text x={dims.marginLeft - 5} dy='0.32em'>{category}</text>
<g class='tick' opacity='1' transform={`translate(0,${this.props.getCategoryY(cat)})`}>
<text x={dims.marginLeft - 5} dy='0.32em'>{cat}</text>
</g>
</React.Fragment>
)
}

render () {
const { dims } = this.props
const categories = this.props.features.USE_CATEGORIES
const { dims, categories } = this.props
const categoriesExist = categories && categories.length > 0
const renderedCategories = categoriesExist
? this.props.categories.map((cat, idx) => this.renderCategory(cat, idx))
: this.renderCategory('Events', 0)

return (
<g class='yAxis'>
{categories}
{renderedCategories}
<rect
ref={this.grabRef}
class='drag-grabber'
Expand Down
8 changes: 4 additions & 4 deletions src/components/Toolbar/CategoriesListPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export default ({
<div>
{categories.map(cat => {
return (<li
key={cat.category.replace(/ /g, '_')}
key={cat.id.replace(/ /g, '_')}
className={'filter-filter active'}
style={{ marginLeft: '20px' }}
>
<Checkbox
label={cat.category}
isActive={activeCategories.includes(cat.category)}
onClickCheckbox={() => onCategoryFilter(cat.category)}
label={cat.id}
isActive={activeCategories.includes(cat.id)}
onClickCheckbox={() => onCategoryFilter(cat.id)}
/>
</li>)
})}
Expand Down
51 changes: 31 additions & 20 deletions src/components/presentational/Timeline/Events.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DatetimeBar from './DatetimeBar'
import DatetimeSquare from './DatetimeSquare'
import DatetimeStar from './DatetimeStar'
import Project from './Project'
import { calcOpacity } from '../../../common/utilities'
import { calcOpacity, getEventCategories } from '../../../common/utilities'

function renderDot (event, styles, props) {
return <DatetimeDot
Expand Down Expand Up @@ -60,6 +60,7 @@ function renderStar (event, styles, props) {
const TimelineEvents = ({
events,
projects,
categories,
narrative,
getDatetimeX,
getY,
Expand All @@ -75,14 +76,14 @@ const TimelineEvents = ({
}) => {
const narIds = narrative ? narrative.steps.map(s => s.id) : []

function renderEvent (event) {
function renderEvent (aggregated, event) {
if (narrative) {
if (!(narIds.includes(event.id))) {
return null
}
}

const isDot = (!!event.location && !!event.longitude) || (features.GRAPH_NONLOCATED && event.projectOffset !== -1)

let renderShape = isDot ? renderDot : renderBar
if (event.shape) {
if (event.shape === 'bar') {
Expand All @@ -96,23 +97,33 @@ const TimelineEvents = ({
}
}

const eventY = getY(event)
let colour = event.colour ? event.colour : getCategoryColor(event.category)
const styles = {
fill: colour,
fillOpacity: eventY > 0 ? calcOpacity(1) : 0,
transition: `transform ${transitionDuration / 1000}s ease`
}
const relatedCategories = getEventCategories(event, categories)

return renderShape(event, styles, {
x: getDatetimeX(event.datetime),
y: eventY,
eventRadius,
onSelect: () => onSelect(event),
dims,
highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.filters[features.HIGHLIGHT_GROUPS.filterIndexIndicatingGroup]) : [],
features
})
if (relatedCategories && relatedCategories.length > 0) {
relatedCategories.forEach(cat => {
const eventY = getY({ ...event, category: cat.id })

let colour = event.colour ? event.colour : getCategoryColor(cat.id)
const styles = {
fill: colour,
fillOpacity: eventY > 0 ? calcOpacity(1) : 0,
transition: `transform ${transitionDuration / 1000}s ease`
}

aggregated.push(
renderShape(event, styles, {
x: getDatetimeX(event.datetime),
y: eventY,
eventRadius,
onSelect: () => onSelect(event),
dims,
highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.filters[features.HIGHLIGHT_GROUPS.filterIndexIndicatingGroup]) : [],
features
})
)
})
}
return aggregated
}

let renderProjects = () => null
Expand All @@ -136,7 +147,7 @@ const TimelineEvents = ({
clipPath={'url(#clip)'}
>
{renderProjects()}
{events.map(event => renderEvent(event))}
{events.reduce(renderEvent, [])}
</g>
)
}
Expand Down
30 changes: 25 additions & 5 deletions src/reducers/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import initial from '../store/initial.js'
import { ASSOCIATION_MODES } from '../common/constants'
import { toggleFlagAC } from '../common/utilities'

import {
Expand All @@ -22,6 +23,7 @@ import {
FETCH_SOURCE_ERROR,
SET_LOADING,
SET_NOT_LOADING,
SET_INITIAL_CATEGORIES,
UPDATE_SEARCH_QUERY
} from '../actions'

Expand Down Expand Up @@ -113,21 +115,22 @@ function toggleFilter (appState, action) {
if (!(action.value instanceof Array)) {
action.value = [action.value]
}
const { filter: associationType } = action

let newFilters = appState.associations.filters.slice(0)
let newAssociations = appState.associations[associationType].slice(0)
action.value.forEach(vl => {
if (newFilters.includes(vl)) {
newFilters = newFilters.filter(s => s !== vl)
if (newAssociations.includes(vl)) {
newAssociations = newAssociations.filter(s => s !== vl)
} else {
newFilters.push(vl)
newAssociations.push(vl)
}
})

return {
...appState,
associations: {
...appState.associations,
filters: newFilters
[associationType]: newAssociations
}
}
}
Expand Down Expand Up @@ -218,6 +221,21 @@ function setNotLoading (appState) {
}
}

function setInitialCategories (appState, action) {
const categories = action.values.reduce((acc, val) => {
if (val.mode === ASSOCIATION_MODES.CATEGORY) acc.push(val.id)
return acc
}, [])

return {
...appState,
associations: {
...appState.associations,
categories: categories
}
}
}

function updateSearchQuery (appState, action) {
return {
...appState,
Expand Down Expand Up @@ -269,6 +287,8 @@ function app (appState = initial.app, action) {
return setLoading(appState)
case SET_NOT_LOADING:
return setNotLoading(appState)
case SET_INITIAL_CATEGORIES:
return setInitialCategories(appState, action)
case UPDATE_SEARCH_QUERY:
return updateSearchQuery(appState, action)
default:
Expand Down
Loading

0 comments on commit 488a5d0

Please sign in to comment.