publish-js: allow deletion and editing of comments

Adds interface affordances for the addition and removal of comments.
This commit is contained in:
Liam Fitzgerald 2020-04-14 20:20:59 +10:00
parent fa92d1cb7f
commit d63cc0d7de
3 changed files with 163 additions and 24 deletions

View File

@ -0,0 +1,26 @@
import React from "react";
export const CommentInput = React.forwardRef((props, ref) => (
<textarea
{...props}
ref={ref}
style={{ resize: "vertical" }}
id="comment"
name="comment"
placeholder="Leave a comment here"
className={
"f9 db border-box w-100 ba b--gray3 pt3 ph3 br1 " +
"b--gray2-d mb2 focus-b--black focus-b--white-d white-d bg-gray0-d"
}
aria-describedby="comment-desc"
style={{ height: "4rem" }}
onKeyDown={e => {
if (
(e.getModifierState("Control") || event.metaKey) &&
e.key === "Enter"
) {
props.onSubmit();
}
}}
></textarea>
));

View File

@ -1,11 +1,21 @@
import React, { Component } from 'react';
import moment from 'moment';
import { Sigil } from './icons/sigil';
import { CommentInput } from './comment-input';
import { uxToHex, cite } from '../../lib/util';
import { Spinner } from './icons/icon-spinner';
export class CommentItem extends Component {
constructor(props){
super(props);
this.state = {
editing: false,
commentBody: ''
};
this.commentChange = this.commentChange.bind(this);
this.commentEdit = this.commentEdit.bind(this);
moment.updateLocale('en', {
relativeTime: {
past: function(input) {
@ -28,6 +38,31 @@ export class CommentItem extends Component {
}
});
}
commentEdit() {
let commentPath = Object.keys(this.props.comment)[0];
let commentBody = this.props.comment[commentPath].content;
this.setState({ editing: true, commentBody });
}
focusTextArea(text) {
text && text.focus();
}
commentChange(e) {
this.setState({
commentBody: e.target.value
})
}
cancelEdit() {
this.setState({ editing: false, commentBody: '' })
}
onUpdate() {
this.props.onUpdate(this.state.commentBody);
}
render() {
let pending = !!this.props.pending ? "o-60" : "";
let commentData = this.props.comment[Object.keys(this.props.comment)[0]];
@ -55,8 +90,13 @@ export class CommentItem extends Component {
name = cite(commentData.author);
}
const { editing } = this.state;
const disabled = this.props.pending
|| window.ship !== commentData.author.slice(1);
return (
<div className={pending}>
<div className={"mb8 " + pending}>
<div className="flex mv3 bg-white bg-gray0-d">
<Sigil
ship={commentData.author}
@ -70,10 +110,43 @@ export class CommentItem extends Component {
{name}
</div>
<div className="f9 gray3 pt1">{date}</div>
{ !editing && !disabled && (
<>
<div onClick={this.commentEdit} className="green2 pointer ml2 f9 pt1">
Edit
</div>
<div onClick={this.props.onDelete} className="red2 pointer ml2 f9 pt1">
Delete
</div>
</>
) }
{ editing && (
<>
<div onClick={this.cancelEdit.bind(this)} className="red2 pointer ml2 f9 pt1">
Cancel
</div>
</>
) }
</div>
<div className="f8 lh-solid mb8 mb2">
{content}
<div className="f8 lh-solid mb2">
{ !editing && content }
{ editing && (
<CommentInput style={{resize:'vertical'}}
ref={(el) => {this.focusTextArea(el)}}
onChange={this.commentChange}
value={this.state.commentBody}
onSubmit={this.onUpdate.bind(this)}>
</CommentInput>
)}
</div>
{ editing && (
<div onClick={this.onUpdate.bind(this)} className="green2 pointer f9 pt1 b--green2 ba pa2 dib">
Submit
</div>
)}
</div>
)
}

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react'
import { CommentItem } from './comment-item';
import { CommentInput } from './comment-input';
import { dateToDa } from '/lib/util';
import { Spinner } from './icons/icon-spinner';
@ -8,11 +9,12 @@ export class Comments extends Component {
super(props);
this.state = {
commentBody: '',
disabled: false,
pending: new Set()
pending: new Set(),
awaiting: null,
}
this.commentSubmit = this.commentSubmit.bind(this);
this.commentChange = this.commentChange.bind(this);
}
componentDidUpdate(prevProps) {
@ -50,10 +52,10 @@ export class Comments extends Component {
this.setState({pending: pendingState});
this.textArea.value = '';
this.setState({commentBody: "", disabled: true});
this.setState({commentBody: "", awaiting: 'new'});
let submit = window.api.action("publish", "publish-action", comment);
submit.then(() => {
this.setState({ disabled: false });
this.setState({ awaiting: null });
})
}
@ -63,6 +65,45 @@ export class Comments extends Component {
})
}
commentUpdate(idx, body) {
let path = Object.keys(this.props.comments[idx])[0];
let comment = {
"edit-comment": {
who: this.props.ship.slice(1),
book: this.props.book,
note: this.props.note,
body: body,
comment: path
}
};
this.setState({ awaiting: 'edit' })
window.api
.action('publish', 'publish-action', comment)
.then(() => { this.setState({ awaiting: null }) })
}
commentDelete(idx) {
let path = Object.keys(this.props.comments[idx])[0];
let comment = {
"del-comment": {
who: this.props.ship.slice(1),
book: this.props.book,
note: this.props.note,
comment: path
}
};
this.setState({ awaiting: { kind: 'del', what: idx }})
window.api
.action('publish', 'publish-action', comment)
.then(() => { this.setState({ awaiting: null }) })
}
render() {
if (!this.props.enabled) {
return null;
@ -93,43 +134,42 @@ export class Comments extends Component {
comment={com}
key={i}
contacts={this.props.contacts}
onUpdate={u => this.commentUpdate(i, u)}
onDelete={() => this.commentDelete(i)}
disabled={!!this.state.awaiting}
/>
);
})
let disableComment = ((this.state.commentBody === '') || (this.state.disabled === true));
let disableComment = ((this.state.commentBody === '') || (!!this.state.awaiting));
let commentClass = (disableComment)
? "bg-transparent f9 pa2 br1 ba b--gray2 gray2"
: "bg-transparent f9 pa2 br1 ba b--gray2 black white-d pointer";
let spinnerText =
this.state.awaiting === 'new'
? 'Posting commment...'
: this.state.awaiting === 'edit'
? 'Updating comment...'
: 'Deleting comment...';
return (
<div>
<div className="mv8 relative">
<div>
<textarea style={{resize:'vertical'}}
<CommentInput style={{resize:'vertical'}}
ref={(el) => {this.textArea = el}}
id="comment"
name="comment"
placeholder="Leave a comment here"
className={"f9 db border-box w-100 ba b--gray3 pt3 ph3 br1 " +
"b--gray2-d mb2 focus-b--black focus-b--white-d white-d bg-gray0-d"}
aria-describedby="comment-desc"
style={{height: "4rem"}}
onChange={this.commentChange}
onKeyDown={(e) => {
if ((e.getModifierState("Control") || event.metaKey)
&& e.key === "Enter") {
this.commentSubmit();
}
}}>
</textarea>
value={this.state.commentBody}
onSubmit={this.commentSubmit}>
</CommentInput>
</div>
<button disabled={disableComment}
onClick={this.commentSubmit}
className={commentClass}>
Add comment
</button>
<Spinner text="Posting comment..." awaiting={this.state.disabled} classes="absolute bottom-0 right-0 pb2"/>
<Spinner text={spinnerText} awaiting={this.state.awaiting} classes="absolute bottom-0 right-0 pb2"/>
</div>
{pendingArray}
{commentArray}