mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-12 15:01:38 +03:00
publish-js: allow deletion and editing of comments
Adds interface affordances for the addition and removal of comments.
This commit is contained in:
parent
fa92d1cb7f
commit
d63cc0d7de
26
pkg/interface/publish/src/js/components/lib/comment-input.js
Normal file
26
pkg/interface/publish/src/js/components/lib/comment-input.js
Normal 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>
|
||||
));
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user