Skip to content

Commit

Permalink
[SDAN-625] Improve rendering performance of Agenda list (#937)
Browse files Browse the repository at this point in the history
* [SDAN-625] Improve rendering performance of Agenda list

* Add missing props

* Further performance enhancements

* Fix lint error

* Remove this.dom.list = null

* Improve checking diff
  • Loading branch information
MarkLark86 committed Dec 17, 2019
1 parent 42744e3 commit 9f4c0c3
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 159 deletions.
7 changes: 3 additions & 4 deletions assets/agenda/components/AgendaApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ class AgendaApp extends BaseApp {


this.props.fetchItems();

}

render() {
Expand Down Expand Up @@ -170,7 +169,7 @@ class AgendaApp extends BaseApp {
{this.state.withSidebar && (
<span
className='content-bar__menu content-bar__menu--nav--open'
ref={(elem) => this.elemOpen = elem}
ref={this.setOpenRef}
title={gettext('Close filter panel')}
onClick={this.toggleSidebar}>
<i className='icon--close-thin icon--white' />
Expand All @@ -180,7 +179,7 @@ class AgendaApp extends BaseApp {
{!this.state.withSidebar && !this.props.bookmarks && (
<span
className='content-bar__menu content-bar__menu--nav'
ref={(elem) => this.elemClose = elem}
ref={this.setCloseRef}
title={gettext('Open filter panel')}
onClick={this.toggleSidebar}>
<i className='icon--hamburger' />
Expand Down Expand Up @@ -262,7 +261,7 @@ class AgendaApp extends BaseApp {
actions={this.props.actions}
activeView={this.props.activeView}
onScroll={this.onListScroll}
refNode={(node) => this.elemList = node}
refNode={this.setListRef}
/>
</div>

Expand Down
134 changes: 134 additions & 0 deletions assets/agenda/components/AgendaListCoverageItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {get, isEqual} from 'lodash';

import {formatDate, formatTime, gettext} from '../../utils';
import {
getCoverageDisplayName,
getCoverageIcon,
getCoverageStatusText,
isCoverageBeingUpdated,
isCoverageForExtraDay,
isWatched,
WORKFLOW_COLORS,
WORKFLOW_STATUS,
} from '../utils';

const getCoverageTootip = (coverage, beingUpdated) => {
let slugline = coverage.item_slugline || coverage.slugline;

slugline = gettext(' coverage{{slugline}}', {slugline: slugline ? ` '${slugline}'` : ''}) ;

if (coverage.workflow_status === WORKFLOW_STATUS.DRAFT) {
return gettext('{{ type }}{{ slugline }} {{ status_text }}', {
type: getCoverageDisplayName(coverage.coverage_type),
slugline: slugline,
status_text: getCoverageStatusText(coverage)
});
}

if (['assigned'].includes(coverage.workflow_status)) {
return gettext('Planned {{ type }} {{ slugline }}, expected {{date}} at {{time}}', {
type: getCoverageDisplayName(coverage.coverage_type),
slugline: slugline,
date: formatDate(coverage.scheduled),
time: formatTime(coverage.scheduled)
});
}

if (['active'].includes(coverage.workflow_status)) {
return gettext('{{ type }} {{ slugline }} in progress, expected {{date}} at {{time}}', {
type: getCoverageDisplayName(coverage.coverage_type),
slugline: slugline,
date: formatDate(coverage.scheduled),
time: formatTime(coverage.scheduled)
});
}

if (coverage.workflow_status === WORKFLOW_STATUS.CANCELLED) {
return gettext('{{ type }} {{slugline}} cancelled', {
type: getCoverageDisplayName(coverage.coverage_type),
slugline: slugline,
});
}

if (coverage.workflow_status === WORKFLOW_STATUS.COMPLETED) {
let deliveryState;
if (get(coverage, 'deliveries.length', 0) > 1) {
deliveryState = beingUpdated ? gettext(' (update to come)') : gettext(' (updated)');
}

return gettext('{{ type }} {{ slugline }} available{{deliveryState}}', {
type: getCoverageDisplayName(coverage.coverage_type),
slugline: slugline,
deliveryState: deliveryState
});
}

return '';
};

class AgendaListCoverageItem extends React.Component {
constructor(props) {
super(props);

this.state = this.getUpdatedState(props);
}

shouldComponentUpdate(nextProps) {
return !isEqual(this.props.coverage, nextProps.coverage);
}

componentWillReceiveProps(nextProps) {
if (!isEqual(this.props.coverage, nextProps.coverage)) {
this.setState(this.getUpdatedState(nextProps));
}
}

getUpdatedState(props) {
const watched = isWatched(props.coverage, props.user);

const state = {
coverageClass: `icon--coverage-${getCoverageIcon(props.coverage.coverage_type)}`,
beingUpdated: isCoverageBeingUpdated(props.coverage),
isWatched: watched,
watchText: watched ? gettext('(Watching)') : '',
isCoverageForExtraDay: isCoverageForExtraDay(props.coverage, props.group),
};

state.tooltip = `${state.watchText} ${getCoverageTootip(props.coverage, state.beingUpdated)}`;

return state;
}

render() {
const props = this.props;
const state = this.state;

return (!props.group ||
(state.isCoverageForExtraDay && props.coverage.planning_id === get(props, 'planningItem.guid'))
) && (
<span
className={classNames('wire-articles__item__icon', {'dashed-border': props.showBorder})}
title={state.tooltip}
>
<i className={`${state.coverageClass} ${WORKFLOW_COLORS[props.coverage.workflow_status]}`}>
{state.beingUpdated && <i className="blue-circle" />}
{state.isWatched &&
<i className="blue-circle blue-circle--pale blue-circle--right" />}
</i>
</span>
);
}
}

AgendaListCoverageItem.propTypes = {
planningItem: PropTypes.object,
user: PropTypes.string,
coverage: PropTypes.object,
showBorder: PropTypes.bool,
group: PropTypes.string,
};

export default AgendaListCoverageItem;
56 changes: 40 additions & 16 deletions assets/agenda/components/AgendaListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ class AgendaListItem extends React.Component {
constructor(props) {
super(props);
this.slugline = props.item.slugline && props.item.slugline.trim();
this.state = {isHover: false, previousVersions: false};

this.state = {previousVersions: false};
this.dom = {article: null};

this.onKeyDown = this.onKeyDown.bind(this);
this.setArticleRef = this.setArticleRef.bind(this);
this.onArticleClick = this.onArticleClick.bind(this);
this.onArticleDoubleClick = this.onArticleDoubleClick.bind(this);
this.onMouseEnter = this.onMouseEnter.bind(this);
}

onKeyDown(event) {
Expand All @@ -43,6 +50,24 @@ class AgendaListItem extends React.Component {
event.preventDefault();
}

setArticleRef(elem) {
this.dom.article = elem;
}

onArticleClick() {
this.props.onClick(this.props.item, this.props.group, this.props.planningId);
}

onArticleDoubleClick() {
this.props.onDoubleClick(this.props.item, this.props.group, this.props.planningId);
}

onMouseEnter() {
if (this.props.actioningItem && this.props.actioningItem._id !== this.props.item._id) {
this.props.resetActioningItem();
}
}

shouldComponentUpdate(nextProps, nextState) {
const {props, state} = this;

Expand All @@ -59,8 +84,8 @@ class AgendaListItem extends React.Component {
}

componentDidMount() {
if (this.props.isActive) {
this.articleElem.focus();
if (this.props.isActive && this.dom.article) {
this.dom.article.focus();
}
}

Expand Down Expand Up @@ -93,7 +118,7 @@ class AgendaListItem extends React.Component {
}

renderListItem(isMobile, children) {
const {item, onClick, onDoubleClick, isExtended, group, planningId} = this.props;
const {item, isExtended, group, planningId} = this.props;
const classes = this.getClassNames(isExtended);
const planningItem = (get(item, 'planning_items') || []).find((p) => p.guid === planningId) || {};
const description = getDescription(item, planningItem);
Expand All @@ -105,16 +130,10 @@ class AgendaListItem extends React.Component {
<article key={item._id}
className={classes.card}
tabIndex='0'
ref={(elem) => this.articleElem = elem}
onClick={() => onClick(item, group, planningId)}
onDoubleClick={() => onDoubleClick(item, group, planningId)}
onMouseEnter={() => {
this.setState({isHover: true});
if (this.props.actioningItem && this.props.actioningItem._id !== item._id) {
this.props.resetActioningItem();
}
}}
onMouseLeave={() => this.setState({isHover: false})}
ref={this.setArticleRef}
onClick={this.onArticleClick}
onDoubleClick={this.onArticleDoubleClick}
onMouseEnter={this.onMouseEnter}
onKeyDown={this.onKeyDown}
>
<div className={classes.wrap}>
Expand All @@ -135,8 +154,13 @@ class AgendaListItem extends React.Component {
{item.headline}</span>}
</h4>

<AgendaListItemIcons item={item} group={group} planningItem={planningItem}
isMobilePhone={isMobile} user={this.props.user} />
<AgendaListItemIcons
item={item}
group={group}
planningItem={planningItem}
isMobilePhone={isMobile}
user={this.props.user}
/>

{(isMobile || isExtended) && description && (
<p className="wire-articles__item__text">
Expand Down
Loading

0 comments on commit 9f4c0c3

Please sign in to comment.