diff --git a/assets/agenda/actions.js b/assets/agenda/actions.js index c9fd7b509..4e98cf15b 100644 --- a/assets/agenda/actions.js +++ b/assets/agenda/actions.js @@ -34,22 +34,22 @@ export function setActive(item) { } export const PREVIEW_ITEM = 'PREVIEW_ITEM'; -export function preview(item) { - return {type: PREVIEW_ITEM, item}; +export function preview(item, group) { + return {type: PREVIEW_ITEM, item, group}; } -export function previewItem(item) { +export function previewItem(item, group) { return (dispatch, getState) => { markItemAsRead(item, getState()); - dispatch(preview(item)); + dispatch(preview(item, group)); item && analytics.itemEvent('preview', item); }; } export const OPEN_ITEM = 'OPEN_ITEM'; -export function openItemDetails(item) { - return {type: OPEN_ITEM, item}; +export function openItemDetails(item, group) { + return {type: OPEN_ITEM, item, group}; } export function requestCoverage(item, message) { @@ -62,10 +62,10 @@ export function requestCoverage(item, message) { }; } -export function openItem(item) { +export function openItem(item, group) { return (dispatch, getState) => { markItemAsRead(item, getState()); - dispatch(openItemDetails(item)); + dispatch(openItemDetails(item, group)); updateRouteParams({ item: item ? item._id : null }, getState()); diff --git a/assets/agenda/components/AgendaApp.jsx b/assets/agenda/components/AgendaApp.jsx index 0355adc1f..011073e58 100644 --- a/assets/agenda/components/AgendaApp.jsx +++ b/assets/agenda/components/AgendaApp.jsx @@ -78,6 +78,7 @@ class AgendaApp extends BaseApp { actions={this.filterActions(this.props.itemToOpen)} onClose={onDetailClose} requestCoverage={this.props.requestCoverage} + group={this.props.previewGroup} />] : [
@@ -190,6 +192,7 @@ AgendaApp.propTypes = { activeFilter: PropTypes.object, createdFilter: PropTypes.object, itemToPreview: PropTypes.object, + previewGroup: PropTypes.string, itemToOpen: PropTypes.object, itemsById: PropTypes.object, modal: PropTypes.object, @@ -231,6 +234,7 @@ const mapStateToProps = (state) => ({ activeFilter: get(state, 'search.activeFilter'), createdFilter: get(state, 'search.createdFilter'), itemToPreview: state.previewItem ? state.itemsById[state.previewItem] : null, + previewGroup: state.previewGroup, itemToOpen: state.openItem ? state.itemsById[state.openItem._id] : null, itemsById: state.itemsById, modal: state.modal, diff --git a/assets/agenda/components/AgendaItemDetails.jsx b/assets/agenda/components/AgendaItemDetails.jsx index 3f9ab41e1..b020696a4 100644 --- a/assets/agenda/components/AgendaItemDetails.jsx +++ b/assets/agenda/components/AgendaItemDetails.jsx @@ -30,7 +30,7 @@ import AgendaCoverages from './AgendaCoverages'; import AgendaAttachments from './AgendaAttachments'; import AgendaCoverageRequest from './AgendaCoverageRequest'; -export default function AgendaItemDetails({item, user, actions, onClose, requestCoverage}) { +export default function AgendaItemDetails({item, user, actions, onClose, requestCoverage, group}) { const locations = getLocations(item); const geoLocations = getGeoLocations(locations); let map = null; @@ -53,7 +53,7 @@ export default function AgendaItemDetails({item, user, actions, onClose, request
- + @@ -85,4 +85,5 @@ AgendaItemDetails.propTypes = { })), onClose: PropTypes.func, requestCoverage: PropTypes.func, + group: PropTypes.string, }; diff --git a/assets/agenda/components/AgendaList.jsx b/assets/agenda/components/AgendaList.jsx index 3e1af6263..6672c6932 100644 --- a/assets/agenda/components/AgendaList.jsx +++ b/assets/agenda/components/AgendaList.jsx @@ -91,7 +91,7 @@ class AgendaList extends React.Component { } } - onItemClick(item) { + onItemClick(item, group) { const itemId = item._id; this.setState({actioningItem: null}); this.cancelPreviewTimeout(); @@ -101,17 +101,17 @@ class AgendaList extends React.Component { this.props.dispatch(setActive(itemId)); if (this.props.previewItem !== itemId) { - this.props.dispatch(previewItem(item)); + this.props.dispatch(previewItem(item, group)); } else { - this.props.dispatch(previewItem(null)); + this.props.dispatch(previewItem(null, null)); } }, CLICK_TIMEOUT); } - onItemDoubleClick(item) { + onItemDoubleClick(item, group) { this.cancelClickTimeout(); this.props.dispatch(setActive(item._id)); - this.props.dispatch(openItem(item)); + this.props.dispatch(openItem(item, group)); } onActionList(event, item, group) { diff --git a/assets/agenda/components/AgendaListItem.jsx b/assets/agenda/components/AgendaListItem.jsx index 3c7c5ceec..4cbf52bce 100644 --- a/assets/agenda/components/AgendaListItem.jsx +++ b/assets/agenda/components/AgendaListItem.jsx @@ -60,8 +60,8 @@ class AgendaListItem extends React.Component { className={cardClassName} tabIndex='0' ref={(elem) => this.articleElem = elem} - onClick={() => onClick(item)} - onDoubleClick={() => onDoubleClick(item)} + onClick={() => onClick(item, group)} + onDoubleClick={() => onDoubleClick(item, group)} onMouseEnter={() => this.setState({isHover: true})} onMouseLeave={() => this.setState({isHover: false})} onKeyDown={this.onKeyDown} diff --git a/assets/agenda/components/AgendaMeta.jsx b/assets/agenda/components/AgendaMeta.jsx index 61a4a98f4..f91d03cbb 100644 --- a/assets/agenda/components/AgendaMeta.jsx +++ b/assets/agenda/components/AgendaMeta.jsx @@ -1,14 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import {hasLocation, getEventLinks, getLocationString, getPublicContacts} from 'agenda/utils'; -import {formatTime, fullDate, isToday} from 'utils'; function AgendaPreviewMeta({item}) { - const dates = [item.dates.start, item.dates.end !== item.dates.start ? item.dates.end : null] - .filter((d) => !!d) - .map((date) => isToday(date) ? formatTime(date) : fullDate(date)); - return (
@@ -16,11 +11,6 @@ function AgendaPreviewMeta({item}) { {getLocationString(item)}
} -
- - {dates.join(' - ')} - -
{getPublicContacts(item).map((contact, index) =>
diff --git a/assets/agenda/components/AgendaPreview.jsx b/assets/agenda/components/AgendaPreview.jsx index 059e1da15..76c6833d9 100644 --- a/assets/agenda/components/AgendaPreview.jsx +++ b/assets/agenda/components/AgendaPreview.jsx @@ -31,7 +31,7 @@ class AgendaPreview extends React.PureComponent { } render() { - const {item, user, actions, openItemDetails, requestCoverage} = this.props; + const {item, user, actions, openItemDetails, requestCoverage, previewGroup} = this.props; const isWatching = get(item, 'watches', []).includes(user); @@ -55,7 +55,7 @@ class AgendaPreview extends React.PureComponent {
this.preview = preview}> - + @@ -83,6 +83,7 @@ AgendaPreview.propTypes = { closePreview: PropTypes.func, openItemDetails: PropTypes.func, requestCoverage: PropTypes.func, + previewGroup: PropTypes.string, }; export default AgendaPreview; diff --git a/assets/agenda/components/AgendaTime.jsx b/assets/agenda/components/AgendaTime.jsx index 9fe43d5af..559c5c7d9 100644 --- a/assets/agenda/components/AgendaTime.jsx +++ b/assets/agenda/components/AgendaTime.jsx @@ -2,16 +2,22 @@ import React from 'react'; import PropTypes from 'prop-types'; import {bem} from 'ui/utils'; -import {formatDate} from 'utils'; +import {formatAgendaDate} from 'utils'; import AgendaListItemLabels from './AgendaListItemLabels'; -import MetaTime from 'ui/components/MetaTime'; -export default function AgendaTime({item}) { +export default function AgendaTime({item, group}) { + const getDates = () => { + const dates = formatAgendaDate(item.dates, group); + if (dates[1]) { + return [
{dates[0]}
, dates[1]]; + } + return dates[0]; + }; + return (
- -
{formatDate(item.dates.start)}
+
{getDates()}
); @@ -19,4 +25,5 @@ export default function AgendaTime({item}) { AgendaTime.propTypes = { item: PropTypes.object.isRequired, + group: PropTypes.string, }; \ No newline at end of file diff --git a/assets/agenda/reducers.js b/assets/agenda/reducers.js index 4e0b52e75..0ffee4b69 100644 --- a/assets/agenda/reducers.js +++ b/assets/agenda/reducers.js @@ -19,6 +19,7 @@ const initialState = { aggregations: null, activeItem: null, previewItem: null, + previewGroup: null, openItem: null, isLoading: false, resultsFiltered: false, diff --git a/assets/reducers.js b/assets/reducers.js index 79ad3b4a8..c88fae9f1 100644 --- a/assets/reducers.js +++ b/assets/reducers.js @@ -103,12 +103,13 @@ export function defaultReducer(state, action) { }; case PREVIEW_ITEM: { - const readItems = getReadItems(state, action.item); + const readItems = getReadItems(state, action.item, action.group); return { ...state, readItems, previewItem: action.item ? action.item._id : null, + previewGroup: action.group, }; } @@ -125,6 +126,7 @@ export function defaultReducer(state, action) { readItems, itemsById, openItem: action.item || null, + previewGroup: action.group || null, }; } diff --git a/assets/styles/index.scss b/assets/styles/index.scss index 8a9749241..bd4ab1a1b 100644 --- a/assets/styles/index.scss +++ b/assets/styles/index.scss @@ -2173,11 +2173,24 @@ a.wire-articles__versions { font-size: .8125rem; &.wire-column__preview__date--event { - padding: 0 10px 10px 20px; + padding: 0 10px 10px 0px; @include xl { - padding: 0 10px 20px 20px; - font-size: 1.125rem; + padding: 0 10px 20px 0px; + font-size: 1rem; + line-height: 1.5rem; + } + } + + &.wire-column__preview__date--dashed-border { + margin-right: 8px; + padding-right: 8px; + border-right: 1px dotted #c4c4c4; + float: left; + + @include xl { + padding-right: 10px; + font-size: 1rem; line-height: 1.5rem; } } diff --git a/assets/utils.js b/assets/utils.js index aa0146af8..964db89cf 100644 --- a/assets/utils.js +++ b/assets/utils.js @@ -13,6 +13,8 @@ const TIME_FORMAT = getConfig('time_format'); const DATE_FORMAT = getConfig('date_format', 'DD-MM-YYYY'); const COVERAGE_DATE_FORMAT = getConfig('coverage_date_format'); const DATETIME_FORMAT = `${TIME_FORMAT} ${DATE_FORMAT}`; +const DAY_IN_MINUTES = 24 * 60 - 1; + /** * Create redux store with default middleware @@ -185,6 +187,39 @@ export function formatDate(dateString) { return parseDate(dateString).format(DATE_FORMAT); } +/** + * Format agenda item start and end dates + * + * @param {String} dateString + * @param {String} group: date of the selected event group + * @return {Array} [time string, date string] + */ +export function formatAgendaDate(agendaDate, group) { + const start = parseDate(agendaDate.start); + const end = parseDate(agendaDate.end); + const duration = end.diff(start, 'minutes'); + const dateGroup = group ? moment(group, DATE_FORMAT) : null; + + if (duration > DAY_IN_MINUTES) { + // Multi day event + return [`(${formatTime(start)} ${formatDate(start)} - ${formatTime(end)} ${formatDate(end)})`, + dateGroup ? formatDate(dateGroup) : '']; + } + + if (duration == DAY_IN_MINUTES) { + // All day event + return [gettext('ALL DAY'), formatDate(start)]; + } + + if (duration == 0) { + // start and end times are the same + return [`${formatTime(start)} ${formatDate(start)}`, '']; + } + + // single day event + return [`${formatTime(start)} - ${formatTime(end)}`, formatDate(start)]; +} + /** * Format coverage date ('HH:mm DD/MM') *