Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance/display important dates on search #41301

Merged
Merged
Changes from 10 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e5611f3
Display the right date all the time
millerf Apr 18, 2020
3cee3d4
Small refactor as post cannot be null (props.isRequired)
millerf Apr 18, 2020
d676b2e
clean up - includeNonDraftStatuses was never used across the project
millerf Apr 18, 2020
36be7c5
Fix CamelCase
millerf Apr 18, 2020
5ca266b
More refactor.
millerf Apr 18, 2020
38dc57d
Refactor label, use only one method to display time label
millerf Apr 18, 2020
32b2d1b
Revert "Fix CamelCase"
millerf Apr 20, 2020
1434be6
Minor translation fix
millerf Apr 20, 2020
aca3eb5
Fix date for pendingPost
millerf Apr 20, 2020
3f0bb96
Fix translation for scheduled posts
millerf Apr 21, 2020
2ed0eed
use for scheduled posts
millerf Apr 21, 2020
7be3d67
Small refactor for readability
millerf Apr 21, 2020
31323cb
Added modified date to pending review status
millerf Apr 22, 2020
dc7e183
Remove unnecessary case in a switch
millerf Apr 24, 2020
75cafee
Remove duplicated on
millerf Apr 24, 2020
964df20
pending as an extra sticker
millerf Apr 24, 2020
00c3126
remove 'on' prefix for dates without text label
millerf Apr 25, 2020
a05f2fe
Change to for draft and pending statuses
millerf Apr 27, 2020
75965c2
Remove negated variable for clarity
millerf Apr 27, 2020
20c0305
refactor getLabel to remove onlySticky and onlyPending parameters
millerf Apr 27, 2020
6157493
rename displayedTime vairables to make more sense
millerf Apr 27, 2020
e651d84
minor JsDocs and formatting fixes
millerf Apr 28, 2020
db74c42
minor changes in variable names
millerf Apr 28, 2020
cb7b07d
Pass private as a label
millerf Apr 28, 2020
5defad5
fix translations
millerf Apr 28, 2020
6b0fcda
Add forgotten case for 'private' posts date
millerf Apr 29, 2020
effba2a
Add better comments for translators for dates
millerf Apr 29, 2020
edd82c6
Update client/my-sites/post-relative-time-status/index.jsx
millerf Apr 30, 2020
0380076
Update client/my-sites/post-relative-time-status/index.jsx
millerf Apr 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 95 additions & 66 deletions client/my-sites/post-relative-time-status/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import PropTypes from 'prop-types';
import { localize } from 'i18n-calypso';
import React from 'react';
import { includes } from 'lodash';

/**
* Internal dependencies
Expand All @@ -21,118 +20,150 @@ class PostRelativeTime extends React.PureComponent {
static propTypes = {
showPublishedStatus: PropTypes.bool.isRequired,
post: PropTypes.object.isRequired,
includeNonDraftStatuses: PropTypes.bool,
link: PropTypes.string,
target: PropTypes.string,
gridiconSize: PropTypes.number,
};

static defaultProps = {
includeNonDraftStatuses: false,
link: null,
target: null,
};

/**
* Get the date to be displayed depending on the status of the post
*/
getTimestamp() {
switch ( this.props.post.status ) {
case 'new':
return null;

case 'draft':
case 'future':
case 'trash':
case 'pending':
return this.props.post.modified;

case 'future':
mmtr marked this conversation as resolved.
Show resolved Hide resolved
default:
return this.props.post.date;
}
}

getDisplayedTimeFromPost( post ) {
getDisplayedTimeForLabel() {
const moment = this.props.moment;

const now = moment();
const scheduledDate = moment( this.getTimestamp() );

if ( ! post ) {
// Placeholder text: "a few seconds ago" in English locale
return now.fromNow();
const isScheduledPost = this.props.post.status === 'future';
if ( ! isScheduledPost && Math.abs( now.diff( this.getTimestamp(), 'days' ) ) < 7 ) {
const time = moment( this.getTimestamp() );
return time.fromNow();
}

const { status, modified, date } = post;
const time = moment( includes( [ 'draft', 'pending', 'future' ], status ) ? modified : date );
if ( now.diff( time, 'days' ) >= 7 ) {
// Like "Mar 15, 2013 6:23 PM" in English locale
return time.format( 'lll' );
}

// Like "3 days ago" in English locale
return time.fromNow();
const sameElseForScheduled = this.props.translate( '[for] ll [at] LT', {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
comment:
'll refers to date (eg. 21 Apr) for when the post will be published & LT refers to time (eg. 18:00) - "at" and "for" is translated',
} );

const sameElse = this.props.translate( '[on] ll [at] LT', {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
comment:
'll refers to date (eg. 21 Apr) & LT refers to time (eg. 18:00) - "at" and "on" is translated',
} );

const scheduledTime = scheduledDate.calendar( null, {
nextDay: this.props.translate( '[tomorrow at] LT', {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
comment: 'LT refers to time (eg. 18:00)',
} ),
sameElse: isScheduledPost ? sameElseForScheduled : sameElse,
} );

// If the content is scheduled to be release within a year, do not display the year at the end
return scheduledDate.diff( now, 'years' ) > 0
? scheduledTime
: scheduledTime.replace( scheduledDate.format( 'Y' ), '' );
}

getTimeText() {
const time = this.getTimestamp();
if ( ! time ) {
return null;
}

return (
<span className="post-relative-time-status__time">
<Gridicon icon="time" size={ this.props.gridiconSize || 18 } />
<time className="post-relative-time-status__time-text" dateTime={ time }>
{ this.getDisplayedTimeFromPost( this.props.post ) }
</time>
{ time && (
<>
<Gridicon icon="time" size={ this.props.gridiconSize || 18 } />
<time className="post-relative-time-status__time-text" dateTime={ time }>
{ this.getDisplayedTimeForLabel() }
</time>
</>
) }
</span>
);
}

getStatusText() {
/**
* Get status label
*
mmtr marked this conversation as resolved.
Show resolved Hide resolved
* @param {boolean} onlySticky sends back the "sticky" label. Special case as is using the same template but for is unrelated to the status
mmtr marked this conversation as resolved.
Show resolved Hide resolved
*
*/
getStatusText( onlySticky = false ) {
const status = this.props.post.status;
let statusClassName = 'post-relative-time-status__status';
let statusIcon = 'aside';
let statusText;

if ( this.props.post.sticky ) {
if ( onlySticky ) {
statusText = this.props.translate( 'sticky' );
statusClassName += ' is-sticky';
statusIcon = 'bookmark-outline';
} else if ( status === 'pending' ) {
statusText = this.props.translate( 'pending review' );
statusClassName += ' is-pending';
} else if ( status === 'future' ) {
const moment = this.props.moment;
const now = moment();
const scheduledDate = moment( this.props.post.date );
// If the content is scheduled to be release within a year, do not display the year at the end
const scheduledTime = scheduledDate.calendar( null, {
sameElse: this.props.translate( 'll [at] LT', {
comment:
'll refers to date (eg. 21 Apr) & LT refers to time (eg. 18:00) - "at" is translated',
} ),
} );

const displayScheduleTime =
scheduledDate.diff( now, 'years' ) > 0
? scheduledTime
: scheduledTime.replace( scheduledDate.format( 'Y' ), '' );

statusText = this.props.translate( 'scheduled for %(displayScheduleTime)s', {
comment: '%(displayScheduleTime)s is when a scheduled post is set to be published',
} else if ( status === 'trash' ) {
statusClassName += ' is-trash';
statusIcon = 'trash';
const displayScheduleTime = this.getDisplayedTimeForLabel();
statusText = this.props.translate( 'trashed %(displayScheduleTime)s', {
Copy link
Member

@mmtr mmtr Apr 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Automattic/i18n or @akirk can confirm, but I think we need to handle both status and date on the same translation. For instance, here displayScheduleTime contains on [date] for posts trashed more than a week ago, so trashed %(displayScheduleTime)s finally reads as trashed on [date]. Some languages might need to move the translated "on" part before "trashed" (while leaving the date after). To support those languages, the translatable string should be trashed on [date] rather than trashed [datePrefixedWithOn].

The same applies for the rest of statuses (scheduled for [date], published on [date], draft last modified on [date], ...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite complex to tackle, as the "on" is not always present (for example : "Published yesterday" vs "Published on Mar, 23 march 2020")
And as translation texts cannot have variables to pass on the status, I would have to have to list all cases that could happen.

Also waiting to translators to confirm, but I think the "on" is the beginning of the proposition of time, so it correlates more with the date than the adjective. As in "On 23rd of March, I published, etc,..." But I might be mistaken...

comment: '%(displayScheduleTime)s is when a post or page was trashed',
args: {
displayScheduleTime,
},
} );
} else if ( status === 'future' ) {
statusClassName += ' is-scheduled';
statusIcon = 'calendar';
} else if ( status === 'trash' ) {
statusText = this.props.translate( 'trashed' );
statusClassName += ' is-trash';
statusIcon = 'trash';
} else if ( this.props.includeBasicStatus ) {
if ( status === 'draft' ) {
statusText = this.props.translate( 'draft' );
} else if ( status === 'publish' ) {
statusText = this.props.translate( 'published' );
} else if ( status === 'new' ) {
statusText = this.props.translate( 'Publish immediately' );
}
const displayScheduleTime = this.getDisplayedTimeForLabel();
statusText = this.props.translate( 'scheduled %(displayScheduleTime)s', {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
comment: '%(displayScheduleTime)s is when a scheduled post or page is set to be published',
args: {
displayScheduleTime,
},
} );
} else if ( status === 'draft' ) {
const displayScheduleTime = this.getDisplayedTimeForLabel();
millerf marked this conversation as resolved.
Show resolved Hide resolved
statusText = this.props.translate( 'draft, last modified %(displayScheduleTime)s', {
comment: '%(displayScheduleTime)s is when a draft post or page was last modified',
args: {
displayScheduleTime,
},
} );
} else if ( status === 'publish' ) {
const displayScheduleTime = this.getDisplayedTimeForLabel();
statusText = this.props.translate( 'published, last modified %(displayScheduleTime)s', {
comment: '%(displayScheduleTime)s is when a post or page was last modified',
args: {
displayScheduleTime,
},
} );
} else if ( status === 'private' ) {
const displayScheduleTime = this.getDisplayedTimeForLabel();
statusText = this.props.translate( 'private, last modified %(displayScheduleTime)s', {
comment: '%(displayScheduleTime)s is when a private post or page was last modified',
args: {
displayScheduleTime,
},
} );
} else if ( status === 'new' ) {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
statusText = this.props.translate( 'Publish immediately' );
}

if ( statusText ) {
Expand All @@ -146,16 +177,12 @@ class PostRelativeTime extends React.PureComponent {
}

render() {
const { post, showPublishedStatus } = this.props;
const { showPublishedStatus, post } = this.props;
const timeText = this.getTimeText();
const statusText = this.getStatusText();
const relativeTimeClass = timeText ? 'post-relative-time-status' : null;
const time = this.getTimestamp();

let innerText = (
<span>
{ timeText }
{ ( post.status === 'future' || showPublishedStatus ) && statusText }
{ showPublishedStatus ? this.getStatusText() : timeText }
{ post.sticky && this.getStatusText( true ) }
</span>
);

Expand All @@ -174,6 +201,8 @@ class PostRelativeTime extends React.PureComponent {
);
}

const relativeTimeClass = timeText ? 'post-relative-time-status' : null;
const time = this.getTimestamp();
return (
<div className={ relativeTimeClass } title={ time }>
{ innerText }
Expand Down