Edit Comment UI reflects expired edit comment window

This commit is contained in:
Benjamin Goering
2017-05-03 12:34:22 -07:00
parent b9a3466e2e
commit ae3cbe990b
9 changed files with 194 additions and 15 deletions
@@ -13,6 +13,15 @@
pointer-events: none;
}
.bylineSecondary {
color: #696969;
font-size: 12px;
}
.editedMarker {
font-style: italic;
}
/* element in the top right of the Comment */
.topRight {
float: right;
+69 -5
View File
@@ -39,6 +39,9 @@ class Comment extends React.Component {
constructor(props) {
super(props);
// timeout to keep track of Comment edit window expiration
this.editWindowExpiryTimeout = null;
this.onClickEdit = this.onClickEdit.bind(this);
this.state = {
@@ -92,7 +95,13 @@ class Comment extends React.Component {
user: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired
}).isRequired
}).isRequired,
editing: PropTypes.shape({
edited: PropTypes.bool,
// ISO8601
editableUntil: PropTypes.string,
})
}).isRequired,
// given a comment, return whether it should be rendered as ignored
@@ -116,6 +125,26 @@ class Comment extends React.Component {
this.setState({isEditing: true});
}
componentDidMount() {
if (this.editWindowExpiryTimeout) {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
// if still in the edit window, set a timeout to re-render once it expires
const msLeftToEdit = editWindowRemainingMs(this.props.comment);
if (msLeftToEdit > 0) {
this.editWindowExpiryTimeout = setTimeout(() => {
// re-render
this.setState(this.state);
}, msLeftToEdit);
}
}
componentWillUnmount() {
if (this.editWindowExpiryTimeout) {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
}
render () {
const {
comment,
@@ -193,7 +222,14 @@ class Comment extends React.Component {
{ commentIsBest(comment)
? <TagLabel><BestIndicator /></TagLabel>
: null }
<PubDate created_at={comment.created_at} />
<span className={styles.bylineSecondary}>
<PubDate created_at={comment.created_at} />
{
(comment.editing && comment.editing.edited)
? <span>&nbsp;<span className={styles.editedMarker}>(Edited)</span></span>
: null
}
</span>
<Slot fill="commentInfoBar" comment={comment} commentId={comment.id} inline/>
{ (currentUser &&
@@ -201,9 +237,12 @@ class Comment extends React.Component {
/* User can edit/delete their own comment for a short window after posting */
? <span className={classnames(styles.topRight)}>
<a
className={classnames(styles.link, {[styles.active]: this.state.isEditing})}
onClick={this.onClickEdit}>Edit</a>
{
commentIsStillEditable(comment) &&
<a
className={classnames(styles.link, {[styles.active]: this.state.isEditing})}
onClick={this.onClickEdit}>Edit</a>
}
</span>
/* TopRightMenu allows currentUser to ignore other users' comments */
@@ -346,3 +385,28 @@ class Comment extends React.Component {
}
export default Comment;
// return a Date instance representing end of edit window for comment
const getEditableUntilDate = (comment) => {
const editing = comment && comment.editing;
const editableUntil = editing && editing.editableUntil && new Date(Date.parse(editing.editableUntil));
return editableUntil;
};
// return whether the comment is editable
function commentIsStillEditable (comment) {
const editing = comment && comment.editing;
if ( ! editing) {return false;}
const editableUntil = getEditableUntilDate(comment);
const editWindowExpired = (editableUntil - new Date) < 0;
return ! editWindowExpired;
}
// return number of milliseconds before edit window expires
function editWindowRemainingMs (comment) {
const editableUntil = getEditableUntilDate(comment);
if ( ! editableUntil) {return;}
const now = new Date();
const editWindowRemainingMs = (editableUntil - now);
return editWindowRemainingMs;
}
@@ -5,6 +5,12 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations';
const lang = new I18n(translations);
const getEditableUntilDate = (comment) => {
const editing = comment && comment.editing;
const editableUntil = editing && editing.editableUntil && new Date(Date.parse(editing.editableUntil));
return editableUntil;
};
/**
* Renders a Comment's body in such a way that the end-user can edit it and save changes
*/
@@ -23,7 +29,13 @@ export class EditableCommentContent extends React.Component {
// comment that is being edited
comment: PropTypes.shape({
body: PropTypes.string
body: PropTypes.string,
editing: PropTypes.shape({
edited: PropTypes.bool,
// ISO8601
editableUntil: PropTypes.string,
})
}).isRequired,
// logged in user
@@ -41,6 +53,22 @@ export class EditableCommentContent extends React.Component {
constructor(props) {
super(props);
this.editComment = this.editComment.bind(this);
this.editWindowExpiryTimeout = null;
}
componentDidMount() {
const editableUntil = getEditableUntilDate(this.props.comment);
const now = new Date();
const editWindowRemainingMs = editableUntil && (editableUntil - now);
if (editWindowRemainingMs > 0) {
this.editWindowExpiryTimeout = setTimeout(() => {
this.forceUpdate();
}, editWindowRemainingMs);
}
}
componentWillUnmount() {
if (this.editWindowExpiryTimeout) {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
}
async editComment(edit) {
const {editComment, addNotification, stopEditing} = this.props;
@@ -74,6 +102,8 @@ export class EditableCommentContent extends React.Component {
}
render() {
const originalBody = this.props.comment.body;
const editableUntil = getEditableUntilDate(this.props.comment);
const editWindowExpired = (editableUntil - new Date()) < 0;
return (
<div style={{marginBottom: '10px'}}>
<CommentForm
@@ -84,7 +114,7 @@ export class EditableCommentContent extends React.Component {
// should be disabled if user hasn't actually changed their
// original comment
return comment.body !== originalBody;
return (comment.body !== originalBody) && ! editWindowExpired;
}}
saveComment={this.editComment}
bodyLabel={'Edit this comment' /* @TODO (bengo) i18n */}
@@ -92,7 +122,50 @@ export class EditableCommentContent extends React.Component {
submitText={'Save changes' /* @TODO (bengo) i18n */}
saveButtonCStyle="green"
/>
{
editWindowExpired
? <p>You can no longer edit this comment. The time window to do so has expired. Why not post another one?</p>
: <p>You have <CountdownSeconds until={editableUntil} /> to save this Edit. You may save this edit now to reset the clock.</p>
}
</div>
);
}
}
/**
* Countdown the number of seconds until a given Date
*/
class CountdownSeconds extends React.Component {
static propTypes = {
until: PropTypes.instanceOf(Date).isRequired
}
constructor(props) {
super(props);
this.countdownInterval = null;
}
componentDidMount() {
const {until} = this.props;
const now = new Date();
if (until - now > 0) {
this.countdownInterval = setInterval(() => {
// re-render
this.forceUpdate();
}, 1000);
}
}
componentWillUnmount() {
if (this.countdownInterval) {
this.countdownInterval = clearInterval(this.countdownInterval);
}
}
render() {
const now = new Date();
const {until} = this.props;
const msRemaining = until - now;
const secRemaining = msRemaining / 1000;
const wholeSecRemaining = Math.floor(secRemaining);
const plural = secRemaining !== 1;
return <span>{`${wholeSecRemaining} second${plural ? 's' : ''}`}</span>;
}
}
@@ -317,9 +317,7 @@ button.comment__action-button[disabled],
}
.coral-plugin-pubdate-text {
color: #696969;
display: inline-block;
font-size: .75rem;
margin-left: 5px;
}
@@ -15,4 +15,8 @@ fragment commentView on Comment {
action_summaries {
...actionSummaryView
}
editing {
edited
editableUntil
}
}