diff --git a/src/components/:w b/src/components/:w new file mode 100644 index 0000000..acd0476 --- /dev/null +++ b/src/components/:w @@ -0,0 +1,148 @@ +import React from 'react'; + +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import * as actions from '../actions'; +import * as selectors from '../selectors'; + +import MediaOverlay from './presentational/MediaOverlay'; +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); + + this.handleViewSource = this.handleViewSource.bind(this) + this.handleHighlight = this.handleHighlight.bind(this) + this.handleSelect = this.handleSelect.bind(this) + this.handleTagFilter = this.handleTagFilter.bind(this) + this.updateTimerange = this.updateTimerange.bind(this) + + this.eventsById = {} + } + + componentDidMount() { + if (!this.props.app.isMobile) { + this.props.actions.fetchDomain() + .then(domain => this.props.actions.updateDomain(domain)); + } + } + + handleHighlight(highlighted) { + this.props.actions.updateHighlighted((highlighted) ? 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) { + console.log('handleViewSource: to implement in Dashboard.jsx') + 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) + } + } + + handleTagFilter(tag) { + this.props.actions.updateTagFilters(tag); + } + + updateTimerange(timeRange) { + this.props.actions.updateTimeRange(timeRange); + } + + getCategoryColor(category='other') { + return this.props.ui.style.categories[category] || this.props.ui.style.categories['other'] + } + + getNarrativeLinks(event) { + const narrative = this.props.domain.narratives.find(nv => nv.id === event.narrative); + if (narrative) return narrative.byId[event.id]; + return null; + } + + render() { + return ( +
+ this.getCategoryColor(category) + }} + /> + + this.props.actions.updateSelected([])} + getNarrativeLinks={event => this.getNarrativeLinks(event)} + getCategoryColor={category => this.getCategoryColor(category)} + /> + this.getCategoryColor(category) + }} + /> + this.props.actions.toggleInfoPopup()} + /> + + + + + +
+ ); + } +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators(actions, dispatch) + }; +} + +export default connect( + state => state, + mapDispatchToProps, +)(Dashboard); diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 6386c3f..43a1257 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import * as actions from '../actions'; +import MediaOverlay from './MediaOverlay.jsx'; import LoadingOverlay from './presentational/LoadingOverlay'; import Map from './Map.jsx'; import Toolbar from './Toolbar.jsx'; @@ -48,7 +49,6 @@ class Dashboard extends React.Component { } handleViewSource(source) { - console.log('handleViewSource: to implement in Dashboard.jsx') this.props.actions.updateSource(source) } @@ -131,6 +131,13 @@ class Dashboard extends React.Component { notifications={this.props.domain.notifications} onToggle={this.props.actions.markNotificationsRead} /> + {this.props.app.source ? ( + { + this.props.actions.updateSource(null)} + } + /> + ) : null} +
+
+ +
+ {/*
*/} + {/* ciao ciao */} + {/*
*/} +
+ + ) + } +} + +export default MediaOverlay diff --git a/src/reducers/app.js b/src/reducers/app.js index 7fe01be..c8a8ea6 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -8,6 +8,7 @@ import { UPDATE_TAGFILTERS, UPDATE_TIMERANGE, UPDATE_NARRATIVE, + UPDATE_SOURCE, RESET_ALLFILTERS, TOGGLE_LANGUAGE, TOGGLE_MAPVIEW, @@ -117,6 +118,13 @@ function toggleMapView(appState, action) { }); } +function updateSource(appState, action) { + return { + ...appState, + source: action.source + } +} + function fetchError(state, action) { return { ...state, @@ -181,6 +189,8 @@ function app(appState = initial.app, action) { return updateTimeRange(appState, action); case UPDATE_NARRATIVE: return updateNarrative(appState, action); + case UPDATE_SOURCE: + return updateSource(appState, action); case RESET_ALLFILTERS: return resetAllFilters(appState, action); case TOGGLE_LANGUAGE: diff --git a/src/scss/main.scss b/src/scss/main.scss index 866e93e..e7ad5e1 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -7,6 +7,7 @@ @import 'header'; @import 'cardstack'; @import 'narrativecard'; +@import 'mediaoverlay'; @import 'map'; @import 'timeline'; @import 'tag-filters'; diff --git a/src/scss/mediaoverlay.scss b/src/scss/mediaoverlay.scss new file mode 100644 index 0000000..710eecd --- /dev/null +++ b/src/scss/mediaoverlay.scss @@ -0,0 +1,51 @@ +$vimeo-height: 800px; +$vimeo-width: 1000px; + +.mo-overlay { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(230, 230, 230, 0.5); + z-index: 20; +} + +.mo-container { + background-color: transparent; + max-width: 80vw; + min-width: 80vw; + max-height: 90vh; + min-height: 90vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .mo-controls, .mo-media-container { + flex: 1; + display: flex; + justify-content: center; + } +} + +.mo-media-container { + max-height: $vimeo-height; +} + +.mo-controls { + color: white; + width: $vimeo-width; + background-color: black; +} + +.vimeo-iframe { + min-height: $vimeo-height; + max-height: $vimeo-height; + min-width: $vimeo-width; + max-width: $vimeo-width; + border: none; +} diff --git a/src/store/initial.js b/src/store/initial.js index 3121edc..f7a4058 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -1,11 +1,11 @@ const initial = { /* - * The Domain or 'domain' of this state refers to the tree of data - * available for render and display. - * Selections and filters in the 'app' subtree will operate the domain - * in mapStateToProps of the Dashboard, and deterimne which items - * in the domain will get rendered by React - */ + * The Domain or 'domain' of this state refers to the tree of data + * available for render and display. + * Selections and filters in the 'app' subtree will operate the domain + * in mapStateToProps of the Dashboard, and deterimne which items + * in the domain will get rendered by React + */ domain: { events: [], narratives: [], @@ -17,24 +17,25 @@ const initial = { }, /* - * The 'app' subtree of this state determines the data and information to be - * displayed. - * It may refer to those the user interacts with, by selecting, - * fitlering and so on, which ultimately operate on the data to be displayed. - * Additionally, some of the 'app' flags are determined by the config file - * or by the characteristics of the client, browser, etc. - */ + * The 'app' subtree of this state determines the data and information to be + * displayed. + * It may refer to those the user interacts with, by selecting, + * fitlering and so on, which ultimately operate on the data to be displayed. + * Additionally, some of the 'app' flags are determined by the config file + * or by the characteristics of the client, browser, etc. + */ app: { errors: { source: null, }, highlighted: null, selected: [], + source: null, narrative: null, filters: { timerange: [ - d3.timeParse("%Y-%m-%dT%H:%M:%S")("2013-02-23T12:00:00"), - d3.timeParse("%Y-%m-%dT%H:%M:%S")("2016-02-23T12:00:00") + d3.timeParse("%Y-%m-%dT%H:%M:%S")("2013-02-23T12:00:00"), + d3.timeParse("%Y-%m-%dT%H:%M:%S")("2016-02-23T12:00:00") ], tags: [], categories: [], @@ -54,36 +55,36 @@ const initial = { duration: 1576800, active: false }, - { - label: '3 meses', - duration: 129600, - active: false - }, - { - label: '3 días', - duration: 4320, - active: false - }, - { - label: '12 horas', - duration: 720, - active: false - }, - { - label: '2 horas', - duration: 120, - active: false - }, - { - label: '30 min', - duration: 30, - active: false - }, - { - label: '10 min', - duration: 10, - active: false - }], + { + label: '3 meses', + duration: 129600, + active: false + }, + { + label: '3 días', + duration: 4320, + active: false + }, + { + label: '12 horas', + duration: 720, + active: false + }, + { + label: '2 horas', + duration: 120, + active: false + }, + { + label: '30 min', + duration: 30, + active: false + }, + { + label: '10 min', + duration: 10, + active: false + }], features: { USE_TAGS: process.env.features.USE_TAGS, USE_SEARCH: process.env.features.USE_SEARCH @@ -99,10 +100,10 @@ const initial = { }, /* - * The 'ui' subtree of this state refers the state of the cosmetic - * elements of the application, such as color palettes of categories - * as well as dom elements to attach SVG - */ + * The 'ui' subtree of this state refers the state of the cosmetic + * elements of the application, such as color palettes of categories + * as well as dom elements to attach SVG + */ ui: { style: { categories: {