Skip to content

Commit

Permalink
Merge pull request #39 from forensic-architecture/topic/guided-narrat…
Browse files Browse the repository at this point in the history
…ives

Topic/guided narratives
  • Loading branch information
breezykermo authored Dec 10, 2018
2 parents 1bae449 + 8c5edb2 commit 7b5c20b
Show file tree
Hide file tree
Showing 34 changed files with 646 additions and 402 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
node_modules/
config.js
dev.config.js
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ The application will require to include a few configuration settings. Configurat
| EVENT_EXT | Endpoint for events, which will be concatenated with SERVER_ROOT | String | No |
| EVENT_DESC_ROOT | Endpoint for additional metadata for each individual event, concatenated to SERVER_ROOT | String | Yes |
| CATEGORY_EXT | Endpoint for categories, concatenated with SERVER_ROOT | String | Yes |
| NARRATIVE_EXT | Endpoint for narratives, concatenated with SERVER_ROOT | String | No |
| TAG_TREE_EXT | Endpoint for tags, concatenated with SERVER_ROOT | String | Yes |
| SITES_EXT | Endpoint for sites, concatenated with SERVER_ROOT | String | Yes |
| MAP_ANCHOR | Geographic coordinates for original map anchor | Array of numbers | No |
Expand Down
1 change: 1 addition & 0 deletions example.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
EVENT_EXT: '/api/example/export_events/rows',
CATEGORY_EXT: '/api/example/export_categories/rows',
SOURCES_EXT: '/api/example/export_events/ids',
NARRATIVE_EXT: '/api/example/export_narratives/rows',
TAGS_EXT: '/api/example/export_tags/tree',
SITES_EXT: '/api/example/export_sites/rows',
MAP_ANCHOR: [31.356397, 34.784818],
Expand Down
42 changes: 33 additions & 9 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ function urlFromEnv(ext) {
}

// TODO: relegate these URLs entirely to environment variables
const EVENT_DATA_URL = urlFromEnv('EVENT_EXT')
const CATEGORY_URL = urlFromEnv('CATEGORY_EXT')
const TAG_URL = urlFromEnv('TAGS_EXT')
const SOURCES_URL = urlFromEnv('SOURCES_EXT')
const SITES_URL = urlFromEnv('SITES_EXT')
const eventUrlMap = (event) => `${process.env.SERVER_ROOT}${process.env.EVENT_DESC_ROOT}/${(event.id) ? event.id : event}`
const EVENT_DATA_URL = urlFromEnv('EVENT_EXT');
const CATEGORY_URL = urlFromEnv('CATEGORY_EXT');
const TAG_URL = urlFromEnv('TAGS_EXT');
const SOURCES_URL = urlFromEnv('SOURCES_EXT');
const NARRATIVE_URL = urlFromEnv('NARRATIVE_EXT');
const SITES_URL = urlFromEnv('SITES_EXT');
const eventUrlMap = (event) => `${process.env.SERVER_ROOT}${process.env.EVENT_DESC_ROOT}/${(event.id) ? event.id : event}`;

/*
* Create an error notification object
Expand Down Expand Up @@ -52,6 +53,10 @@ export function fetchDomain () {
.then(response => response.json())
.catch(handleError('categories'))

const narPromise = fetch(NARRATIVE_URL)
.then(response => response.json())
.catch(handleError('narratives'))

let sitesPromise = Promise.resolve([])
if (process.env.features.USE_SITES) {
sitesPromise = fetch(SITES_URL)
Expand All @@ -66,14 +71,16 @@ export function fetchDomain () {
.catch(handleError('tags'))
}

return Promise.all([ eventPromise, catPromise, sitesPromise, tagsPromise])
return Promise.all([eventPromise, catPromise, narPromise,
sitesPromise, tagsPromise])
.then(response => {
dispatch(toggleFetchingDomain())
const result = {
events: response[0],
categories: response[1],
sites: response[2],
tags: response[3],
narratives: response[2],
sites: response[3],
tags: response[4],
notifications
}
return result
Expand Down Expand Up @@ -102,6 +109,7 @@ export function updateDomain(domain) {
categories: domain.categories,
tags: domain.tags,
sites: domain.sites,
narratives: domain.narratives,
notifications: domain.notifications
}
}
Expand Down Expand Up @@ -156,6 +164,14 @@ export function updateTagFilters(tag) {
}
}

export const UPDATE_NARRATIVE = 'UPDATE_NARRATIVE';
export function updateNarrative(narrative) {
return {
type: UPDATE_NARRATIVE,
narrative
}
}

export const UPDATE_TIMERANGE = 'UPDATE_TIMERANGE';
export function updateTimeRange(timerange) {
return {
Expand Down Expand Up @@ -209,6 +225,14 @@ export function toggleInfoPopup() {
}
}

export const TOGGLE_MAPVIEW = 'TOGGLE_MAPVIEW';
export function toggleMapView(layer) {
return {
type: TOGGLE_MAPVIEW,
layer
}
}

export const TOGGLE_NOTIFICATIONS = 'TOGGLE_NOTIFICATIONS'
export function toggleNotifications() {
return {
Expand Down
10 changes: 7 additions & 3 deletions src/components/Card.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import copy from '../js/data/copy.json';
import {isNotNullNorUndefined} from '../js/data/utilities';
import {
isNotNullNorUndefined,
parseDate,
formatterWithYear
} from '../js/utilities';
import React from 'react';

import Spinner from './presentational/Spinner';
Expand Down Expand Up @@ -35,8 +39,8 @@ class Card extends React.Component {

makeTimelabel(timestamp) {
if (timestamp === null) return null;
const parsedTimestamp = this.props.tools.parser(timestamp);
const timelabel = this.props.tools.formatterWithYear(parsedTimestamp);
const parsedTimestamp = parseDate(timestamp);
const timelabel = formatterWithYear(parsedTimestamp);
return timelabel;
}

Expand Down
9 changes: 4 additions & 5 deletions src/components/CardStack.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Card from './Card.jsx';
import copy from '../js/data/copy.json';
import {
isNotNullNorUndefined
} from '../js/data/utilities.js';
} from '../js/utilities.js';

class CardStack extends React.Component {

Expand All @@ -21,7 +21,7 @@ class CardStack extends React.Component {
<Card
event={event}
language={this.props.language}
tools={this.props.tools}
isLoading={this.props.isLoading}
getNarrativeLinks={this.props.getNarrativeLinks}
getCategoryGroup={this.props.getCategoryGroup}
getCategoryColor={this.props.getCategoryColor}
Expand Down Expand Up @@ -90,9 +90,8 @@ function mapStateToProps(state) {
return {
selected: state.app.selected,
language: state.app.language,
tools: state.ui.tools,
isCardstack: state.ui.flags.isCardstack,
isFetchingSources: state.ui.flags.isFetchingSources
isCardstack: state.app.flags.isCardstack,
isLoading: state.app.flags.isFetchingEvents
}
}

Expand Down
19 changes: 12 additions & 7 deletions src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import LoadingOverlay from './presentational/LoadingOverlay';
import Viewport from './Viewport.jsx';
import Toolbar from './Toolbar.jsx';
import CardStack from './CardStack.jsx';
import NarrativeCard from './NarrativeCard.js';
import InfoPopUp from './InfoPopup.jsx';
import Timeline from './Timeline.jsx';
import Notification from './Notification.jsx';

import { parseDate } from '../js/utilities';

class Dashboard extends React.Component {
constructor(props) {
super(props);
Expand All @@ -23,7 +26,7 @@ class Dashboard extends React.Component {
this.handleTagFilter = this.handleTagFilter.bind(this);
this.updateTimerange = this.updateTimerange.bind(this);

this.eventsById = {};
this.eventsById = {}
}

componentDidMount() {
Expand All @@ -46,9 +49,7 @@ class Dashboard extends React.Component {
handleSelect(selected) {
if (selected) {
let eventsToSelect = selected.map(event => this.getEventById(event.id));
const p = this.props.ui.tools.parser;

eventsToSelect = eventsToSelect.sort((a, b) => p(a.timestamp) - p(b.timestamp))
eventsToSelect = eventsToSelect.sort((a, b) => parseDate(a.timestamp) - parseDate(b.timestamp))

this.props.actions.fetchSelected(eventsToSelect)
}
Expand All @@ -67,7 +68,7 @@ class Dashboard extends React.Component {
}

getNarrativeLinks(event) {
const narrative = this.props.domain.narratives.find(nv => nv.key === event.narrative);
const narrative = this.props.domain.narratives.find(nv => nv.id === event.narrative);
if (narrative) return narrative.byId[event.id];
return null;
}
Expand Down Expand Up @@ -104,13 +105,17 @@ class Dashboard extends React.Component {
app={this.props.app}
toggle={() => this.props.actions.toggleInfoPopup()}
/>
<NarrativeCard
onSelect={this.handleSelect}
actions={this.props.actions}
/>
<Notification
isNotification={this.props.ui.flags.isNotification}
isNotification={this.props.app.flags.isNotification}
notifications={this.props.domain.notifications}
onToggle={this.props.actions.markNotificationsRead}
/>
<LoadingOverlay
ui={this.props.ui.flags.isFetchingDomain}
ui={this.props.app.flags.isFetchingDomain}
language={this.props.app.language}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/InfoPopup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class InfoPopUp extends React.Component{

renderView2DLegend() {
return (
<div className={`infopopup ${(this.props.ui.flags.isInfopopup) ? '' : 'hidden'}`}>
<div className={`infopopup ${(this.props.app.flags.isInfopopup) ? '' : 'hidden'}`}>
<button onClick={() => this.props.toggle()} className="side-menu-burg over-white is-active"><span /></button>
{this.renderView2DCopy()}
<div className="legend">
Expand Down
71 changes: 71 additions & 0 deletions src/components/NarrativeCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { connect } from 'react-redux'

class NarrativeCard extends React.Component {

constructor() {
super();

this.state = {
step: 0
}
}

goToPrevKeyFrame() {
if (this.state.step > 0) {
this.setState({ step: this.state.step - 1 });
}
}

goToNextKeyFrame() {
if (this.state.step < this.props.narrative.steps.length - 1) {
this.setState({ step: this.state.step + 1 });
}
}

componentDidUpdate() {
if (this.props.narrative !== null) {
const step = this.props.narrative.steps[this.state.step];
this.props.onSelect([step]);
}
}

renderClose() {
return (
<button
className="side-menu-burg is-active"
onClick={() => { this.props.actions.updateNarrative(null); }}
>
<span></span>
</button>
)
}

render() {
if (this.props.narrative !== null && this.props.narrative.steps[this.state.step]) {
const steps = this.props.narrative.steps;
const step = steps[this.state.step];

return (
<div className="narrative-info">
{this.renderClose()}
<h6>{this.props.narrative.label}</h6>
<p>{this.props.narrative.description}</p>
<h3>{this.state.step + 1}/{steps.length}. {step.location}</h3>
<div className="actions">
<div className={`${(!this.state.step) ? 'disabled ' : ''} action`} onClick={() => this.goToPrevKeyFrame()}>&larr;</div>
<div className={`${(this.state.step >= this.props.narrative.steps.length - 1) ? 'disabled ' : ''} action`} onClick={() => this.goToNextKeyFrame()}>&rarr;</div>
</div>
</div>
);
}
return (<div/>);
}
}

function mapStateToProps(state) {
return {
narrative: state.app.narrative
}
}
export default connect(mapStateToProps)(NarrativeCard);
5 changes: 2 additions & 3 deletions src/components/Notification.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export default class Notification extends React.Component{
}

renderNotificationContent(notification) {
const { type, message, items } = notification;
let { type, message, items } = notification;

return (
<div>
<div className={`message ${type}`}>
{`${message}`}
{message}
</div>
<div className={`details ${this.state.isExtended}`}>
{(items !== null) ? this.renderItems(items) : ''}
Expand All @@ -48,7 +48,6 @@ export default class Notification extends React.Component{
return (
<div className={`notification-wrapper`}>
{this.props.notifications.map((notification) => {

return (
<div className='notification' onClick={() => this.toggleDetails() }>
<button
Expand Down
7 changes: 5 additions & 2 deletions src/components/TagListPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class TagListPanel extends React.Component {
}

componentDidMount() {
this.computeTree(this.props.tags.children[this.props.tagType]);
this.computeTree(this.props.tags);//.children[this.props.tagType]);
}

componentWillReceiveProps(nextProps) {
this.computeTree(nextProps.tags.children[nextProps.tagType]);
this.computeTree(nextProps.tags);//.children[nextProps.tagType]);
}

onClickCheckbox(tag) {
Expand Down Expand Up @@ -65,8 +65,11 @@ class TagListPanel extends React.Component {
}

render() {

return (
<div className="react-innertabpanel">
<h2>Explore data by tag</h2>
<p>Explore freely all the data by selecting tags.</p>
{this.renderTree()}
</div>
);
Expand Down
9 changes: 4 additions & 5 deletions src/components/Timeline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import * as selectors from '../selectors';

import copy from '../js/data/copy.json';
import { formatterWithYear } from '../js/utilities';
import TimelineLogic from '../js/timeline/timeline.js';

class Timeline extends React.Component {
Expand All @@ -15,7 +16,6 @@ class Timeline extends React.Component {

componentDidMount() {
const ui = {
tools: this.props.tools,
dom: this.props.dom
}

Expand Down Expand Up @@ -47,8 +47,8 @@ class Timeline extends React.Component {
const labels_title_lang = copy[this.props.app.language].timeline.labels_title;
const info_lang = copy[this.props.app.language].timeline.info;
let classes = `timeline-wrapper ${(this.state.isFolded) ? ' folded' : ''}`;
const date0 = this.props.tools.formatterWithYear(this.props.app.timerange[0]);
const date1 = this.props.tools.formatterWithYear(this.props.app.timerange[1]);
const date0 = formatterWithYear(this.props.app.timerange[0]);
const date1 = formatterWithYear(this.props.app.timerange[1]);

return (
<div className={classes}>
Expand Down Expand Up @@ -82,9 +82,8 @@ function mapStateToProps(state) {
language: state.app.language,
zoomLevels: state.app.zoomLevels
},
tools: state.ui.tools,
dom: state.ui.dom,
}
}

export default connect(mapStateToProps)(Timeline);
export default connect(mapStateToProps)(Timeline);
Loading

0 comments on commit 7b5c20b

Please sign in to comment.