Activity Panel: set reviews unread indicator based on real data (https://github.com/woocommerce/woocommerce-admin/pull/1860)

* Activity Panel: set reviews unread indicator based on real data

* Unify date comparisons to GMT

* Add numberOfReviews propType

* Verify date_created_gmt exists before using it
This commit is contained in:
Albert Juhé Lluveras 2019-03-21 12:35:46 +01:00 committed by GitHub
parent 9038bdfdae
commit 41e5d00708
4 changed files with 120 additions and 36 deletions

View File

@ -92,7 +92,7 @@ class ActivityPanel extends Component {
// @todo Pull in dynamic unread status/count // @todo Pull in dynamic unread status/count
getTabs() { getTabs() {
const { unreadNotes, unreadOrders } = this.props; const { unreadNotes, unreadOrders, unreadReviews } = this.props;
return [ return [
{ {
name: 'inbox', name: 'inbox',
@ -119,7 +119,7 @@ class ActivityPanel extends Component {
name: 'reviews', name: 'reviews',
title: __( 'Reviews', 'woocommerce-admin' ), title: __( 'Reviews', 'woocommerce-admin' ),
icon: <Gridicon icon="star" />, icon: <Gridicon icon="star" />,
unread: false, unread: unreadReviews,
} }
: null, : null,
].filter( Boolean ); ].filter( Boolean );
@ -135,7 +135,8 @@ class ActivityPanel extends Component {
case 'stock': case 'stock':
return <StockPanel />; return <StockPanel />;
case 'reviews': case 'reviews':
return <ReviewsPanel />; const { numberOfReviews } = this.props;
return <ReviewsPanel numberOfReviews={ numberOfReviews } />;
default: default:
return null; return null;
} }
@ -270,12 +271,16 @@ export default withSelect( select => {
getReportItemsError, getReportItemsError,
isGetNotesRequesting, isGetNotesRequesting,
isReportItemsRequesting, isReportItemsRequesting,
getReviews,
getReviewsTotalCount,
getReviewsError,
isGetReviewsRequesting,
} = select( 'wc-api' ); } = select( 'wc-api' );
const userData = getCurrentUserData();
const orderStatuses = wcSettings.wcAdminSettings.woocommerce_actionable_order_statuses || [ const orderStatuses = wcSettings.wcAdminSettings.woocommerce_actionable_order_statuses || [
'processing', 'processing',
'on-hold', 'on-hold',
]; ];
const userData = getCurrentUserData();
const notesQuery = { const notesQuery = {
page: 1, page: 1,
@ -290,10 +295,10 @@ export default withSelect( select => {
new Date( latestNote[ 0 ].date_created_gmt ).getTime() > new Date( latestNote[ 0 ].date_created_gmt ).getTime() >
userData.activity_panel_inbox_last_read; userData.activity_panel_inbox_last_read;
let unreadOrders = null;
if ( ! orderStatuses.length ) { if ( ! orderStatuses.length ) {
return { unreadNotes, unreadOrders: false }; unreadOrders = false;
} } else {
const ordersQuery = { const ordersQuery = {
page: 1, page: 1,
per_page: 0, per_page: 0,
@ -301,18 +306,41 @@ export default withSelect( select => {
}; };
const totalOrders = getReportItems( 'orders', ordersQuery ).totalResults; const totalOrders = getReportItems( 'orders', ordersQuery ).totalResults;
const isError = Boolean( getReportItemsError( 'orders', ordersQuery ) ); const isOrdersError = Boolean( getReportItemsError( 'orders', ordersQuery ) );
const isRequesting = isReportItemsRequesting( 'orders', ordersQuery ); const isOrdersRequesting = isReportItemsRequesting( 'orders', ordersQuery );
let unreadOrders = null; if ( ! isOrdersError && ! isOrdersRequesting ) {
if ( ! isError && ! isRequesting ) {
if ( totalOrders > 0 ) { if ( totalOrders > 0 ) {
unreadOrders = true; unreadOrders = true;
} else { } else {
unreadOrders = false; unreadOrders = false;
} }
} }
}
return { unreadNotes, unreadOrders }; let numberOfReviews = null;
let unreadReviews = false;
if ( 'yes' === wcSettings.reviewsEnabled ) {
const reviewsQuery = {
order: 'desc',
orderby: 'date_gmt',
page: 1,
per_page: 1,
};
const reviews = getReviews( reviewsQuery );
const totalReviews = getReviewsTotalCount( reviewsQuery );
const isReviewsError = Boolean( getReviewsError( reviewsQuery ) );
const isReviewsRequesting = isGetReviewsRequesting( reviewsQuery );
if ( ! isReviewsError && ! isReviewsRequesting ) {
numberOfReviews = totalReviews;
unreadReviews =
reviews.length &&
reviews[ 0 ].date_created_gmt &&
new Date( reviews[ 0 ].date_created_gmt + 'Z' ).getTime() >
userData.activity_panel_reviews_last_read;
}
}
return { unreadNotes, unreadOrders, unreadReviews, numberOfReviews };
} )( clickOutside( ActivityPanel ) ); } )( clickOutside( ActivityPanel ) );

View File

@ -7,8 +7,10 @@ import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import Gridicon from 'gridicons'; import Gridicon from 'gridicons';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import moment from 'moment';
import { noop, isNull } from 'lodash'; import { noop, isNull } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withDispatch } from '@wordpress/data';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -33,7 +35,21 @@ import sanitizeHTML from 'lib/sanitize-html';
import withSelect from 'wc-api/with-select'; import withSelect from 'wc-api/with-select';
class ReviewsPanel extends Component { class ReviewsPanel extends Component {
renderReview( review ) { constructor() {
super();
this.mountTime = new Date().getTime();
}
componentWillUnmount() {
const userDataFields = {
[ 'activity_panel_reviews_last_read' ]: this.mountTime,
};
this.props.updateCurrentUserData( userDataFields );
}
renderReview( review, props ) {
const { lastRead } = props;
const product = const product =
( review && review._embedded && review._embedded.up && review._embedded.up[ 0 ] ) || null; ( review && review._embedded && review._embedded.up && review._embedded.up[ 0 ] ) || null;
@ -109,15 +125,40 @@ class ReviewsPanel extends Component {
key={ review.id } key={ review.id }
title={ title } title={ title }
subtitle={ subtitle } subtitle={ subtitle }
date={ review.date_created } date={
review.date_created_gmt
? moment( review.date_created_gmt + 'Z' ).format( 'YYYY-MM-DDTH:mm:ss' )
: null
}
icon={ icon } icon={ icon }
actions={ cardActions() } actions={ cardActions() }
unread={
! lastRead ||
! review.date_created_gmt ||
new Date( review.date_created_gmt + 'Z' ).getTime() > lastRead
}
> >
<span dangerouslySetInnerHTML={ sanitizeHTML( review.review ) } /> <span dangerouslySetInnerHTML={ sanitizeHTML( review.review ) } />
</ActivityCard> </ActivityCard>
); );
} }
renderPlaceholders() {
const { numberOfReviews } = this.props;
const placeholders = new Array( numberOfReviews );
return placeholders
.fill( 0 )
.map( ( p, i ) => (
<ActivityCardPlaceholder
className="woocommerce-review-activity-card"
key={ i }
hasAction
hasDate
lines={ 2 }
/>
) );
}
render() { render() {
const { isError, isRequesting, reviews } = this.props; const { isError, isRequesting, reviews } = this.props;
@ -148,14 +189,11 @@ class ReviewsPanel extends Component {
<ActivityHeader title={ __( 'Reviews', 'woocommerce-admin' ) } /> <ActivityHeader title={ __( 'Reviews', 'woocommerce-admin' ) } />
<Section> <Section>
{ isRequesting ? ( { isRequesting ? (
<ActivityCardPlaceholder this.renderPlaceholders()
className="woocommerce-review-activity-card"
hasAction
hasDate
lines={ 2 }
/>
) : ( ) : (
<Fragment>{ reviews.map( this.renderReview ) }</Fragment> <Fragment>
{ reviews.map( review => this.renderReview( review, this.props ) ) }
</Fragment>
) } ) }
</Section> </Section>
</Fragment> </Fragment>
@ -167,17 +205,26 @@ ReviewsPanel.propTypes = {
reviews: PropTypes.array.isRequired, reviews: PropTypes.array.isRequired,
isError: PropTypes.bool, isError: PropTypes.bool,
isRequesting: PropTypes.bool, isRequesting: PropTypes.bool,
numberOfReviews: PropTypes.number,
}; };
ReviewsPanel.defaultProps = { ReviewsPanel.defaultProps = {
reviews: [], reviews: [],
isError: false, isError: false,
isRequesting: false, isRequesting: false,
numberOfReviews: 0,
}; };
export default compose( export default compose(
withSelect( select => { withSelect( ( select, props ) => {
const { getReviews, getReviewsError, isGetReviewsRequesting } = select( 'wc-api' ); const { numberOfReviews } = props;
const { getCurrentUserData, getReviews, getReviewsError, isGetReviewsRequesting } = select(
'wc-api'
);
if ( numberOfReviews === 0 ) {
return {};
}
const userData = getCurrentUserData();
const reviewsQuery = { const reviewsQuery = {
page: 1, page: 1,
per_page: QUERY_DEFAULTS.pageSize, per_page: QUERY_DEFAULTS.pageSize,
@ -188,6 +235,13 @@ export default compose(
const isError = Boolean( getReviewsError( reviewsQuery ) ); const isError = Boolean( getReviewsError( reviewsQuery ) );
const isRequesting = isGetReviewsRequesting( reviewsQuery ); const isRequesting = isGetReviewsRequesting( reviewsQuery );
return { reviews, isError, isRequesting }; return { reviews, isError, isRequesting, lastRead: userData.activity_panel_reviews_last_read };
} ),
withDispatch( dispatch => {
const { updateCurrentUserData } = dispatch( 'wc-api' );
return {
updateCurrentUserData,
};
} ) } )
)( ReviewsPanel ); )( ReviewsPanel );

View File

@ -47,6 +47,7 @@ function updateCurrentUserData( resourceNames, data, fetch ) {
'dashboard_leaderboards', 'dashboard_leaderboards',
'dashboard_leaderboard_rows', 'dashboard_leaderboard_rows',
'activity_panel_inbox_last_read', 'activity_panel_inbox_last_read',
'activity_panel_reviews_last_read',
]; ];
if ( resourceNames.includes( resourceName ) ) { if ( resourceNames.includes( resourceName ) ) {

View File

@ -450,6 +450,7 @@ function wc_admin_get_user_data_fields() {
'dashboard_leaderboards', 'dashboard_leaderboards',
'dashboard_leaderboard_rows', 'dashboard_leaderboard_rows',
'activity_panel_inbox_last_read', 'activity_panel_inbox_last_read',
'activity_panel_reviews_last_read',
); );
return apply_filters( 'wc_admin_get_user_data_fields', $user_data_fields ); return apply_filters( 'wc_admin_get_user_data_fields', $user_data_fields );