-
Notifications
You must be signed in to change notification settings - Fork 522
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
Fix Bug 1603610 - Enable users to leave comments on translations #1524
Changes from 46 commits
af6fcef
8cc1297
1d3885a
7f72c2b
7cb311a
22e3cce
0efd8f0
7cdbc07
476afcc
ebf1d67
3df52ab
1fcc776
a6c3939
ab6cf20
7f62e1e
ac452f5
b9fa253
31d9559
bf863cc
917df09
9c3c2af
70aaf6e
c6fa2a7
9deeb20
0cc6b5d
3ac721e
2c2923a
cb62abe
654b5d5
528edef
60898f2
b3ea3b0
902de9d
8762a10
2e34f21
bc7656b
eb2f579
3383171
cc254a3
71b9288
0260b78
633ad4a
6d5ad27
b988f38
95c92f6
4e2e1c4
62cca2b
58e5975
e1bf8e6
95687ee
8adcef8
9e56237
a7a13a7
ad32c94
9230e0c
137eeec
1bccc01
6ad244f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,13 @@ resourceprogress-ResourceProgress--errors = Errors | |
resourceprogress-ResourceProgress--missing = Missing | ||
|
||
|
||
## Comments | ||
## Allows user to leave comments on translations | ||
|
||
comments-AddComment--input = | ||
.placeholder = Write a comment... | ||
|
||
|
||
## Editor Menu | ||
## Allows contributors to modify or propose a translation | ||
|
||
|
@@ -270,6 +277,13 @@ history-Translation--button-not-rejected = | |
history-Translation--button-rejected = | ||
.title = Rejected | ||
|
||
history-Translation--button-comment = { $commentCount -> | ||
[0] Comment | ||
[one] { $commentCount } Comment | ||
*[other] { $commentCount } Comments | ||
} | ||
adngdb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.title = Toggle translation comment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd do "comment" -> "comments". |
||
|
||
|
||
## Interactive Tour | ||
## Shows an interactive Tour on the "Tutorial" project, | ||
|
@@ -436,6 +450,7 @@ notification--make-suggestions-enabled = Make Suggestions enabled | |
notification--make-suggestions-disabled = Make Suggestions disabled | ||
notification--entity-not-found = Can’t load specified string | ||
notification--string-link-copied = Link copied to clipboard | ||
notification--comment-added = Comment added | ||
|
||
|
||
## OtherLocales Translation | ||
|
@@ -567,6 +582,12 @@ search-TimeRangeFilter--edit-range = <glyph></glyph>Edit Range | |
search-TimeRangeFilter--save-range = Save Range | ||
|
||
|
||
## User Avatar | ||
## Shows user Avatar with alt text | ||
|
||
user-UserAvatar--anon-alt-text = Anonymous User | ||
user-UserAvatar--alt-text = User Profile | ||
|
||
## User Menu | ||
## Shows user menu entries and options to sign in or out. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* @flow */ | ||
|
||
import APIBase from './base'; | ||
|
||
|
||
export default class CommentAPI extends APIBase { | ||
add(comment: string, translationId: number) { | ||
const payload = new URLSearchParams(); | ||
payload.append('comment', comment); | ||
payload.append('translationId', translationId.toString()); | ||
|
||
const headers = new Headers(); | ||
const csrfToken = this.getCSRFToken(); | ||
headers.append('X-Requested-With', 'XMLHttpRequest'); | ||
headers.append('X-CSRFToken', csrfToken); | ||
|
||
return this.fetch('/add-comment/', 'POST', payload, headers); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.comments-list .add-comment textarea { | ||
background-color: #272a2f; | ||
border: solid 1px #272a2f; | ||
border-radius: 5px; | ||
color: #AAAAAA; | ||
display: flex; | ||
align-items: center; | ||
height: 1.5em; | ||
padding: 9px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* @flow */ | ||
|
||
import * as React from 'react'; | ||
import { Localized } from '@fluent/react'; | ||
|
||
import './AddComment.css'; | ||
|
||
import { UserAvatar } from 'core/user' | ||
|
||
type Props = {| | ||
user: string, | ||
username: string, | ||
imageURL: string, | ||
translationId: number, | ||
addComment: (string, number) => void, | ||
|}; | ||
|
||
|
||
export default function AddComments(props: Props) { | ||
const { | ||
user, | ||
username, | ||
imageURL, | ||
translationId, | ||
addComment, | ||
} = props; | ||
|
||
let commentInput: any = React.useRef(); | ||
|
||
if (!user) { | ||
return null; | ||
} | ||
|
||
const onEnterSubmit = (event: SyntheticKeyboardEvent<HTMLTextAreaElement>) => { | ||
if (event.keyCode === 13 && event.shiftKey === false) { | ||
event.preventDefault(); | ||
submitComment(event); | ||
} | ||
} | ||
|
||
const submitComment = (event: SyntheticKeyboardEvent<>) => { | ||
event.preventDefault(); | ||
const comment = commentInput.current.value; | ||
|
||
if (!comment) { | ||
return null; | ||
} | ||
|
||
addComment(comment, translationId); | ||
commentInput.current.value = ''; | ||
}; | ||
|
||
return <div className='comment add-comment'> | ||
<UserAvatar | ||
user={ user } | ||
username={ username } | ||
title='' | ||
abowler2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
imageUrl={ imageURL } | ||
/> | ||
<form className='container'> | ||
<Localized | ||
id='comments-AddComment--input' | ||
attrs={{ placeholder: true }} | ||
> | ||
<textarea | ||
id='comment-input' | ||
name='comment' | ||
dir='auto' | ||
placeholder={ `Write a comment ${'\u2026'}` } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No space before the ellipsis, and I believe you can simply use the actual character, no need to use the Unicode representation: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that, whenever you change localized content, what actually matters is what is in the |
||
ref={ commentInput } | ||
onKeyDown={ onEnterSubmit } | ||
/> | ||
</Localized> | ||
</form> | ||
</div> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import sinon from 'sinon'; | ||
|
||
import AddComment from './AddComment'; | ||
|
||
|
||
const DEFAULT_USER = { | ||
user: 'RSwanson', | ||
username: 'Ron_Swanson', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😍 |
||
imageURL: '', | ||
} | ||
|
||
describe('<AddComment>', () => { | ||
it('calls submitComment function', () => { | ||
const submitCommentFn = sinon.spy(); | ||
const wrapper = shallow(<AddComment | ||
{ ...DEFAULT_USER } | ||
submitComment={ submitCommentFn } | ||
/>); | ||
|
||
const event = { | ||
preventDefault: sinon.spy(), | ||
}; | ||
|
||
wrapper.find('form').simulate('submit', event); | ||
expect(submitCommentFn.calledOnce).toBeTruthy; | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
.comments-list .comment a { | ||
color: #7BC876; | ||
font-weight: 400; | ||
} | ||
|
||
.comments-list .comment .content { | ||
background-color: #4d5967; | ||
border: solid 1px #4d5967; | ||
border-radius: 5px; | ||
display: flex; | ||
align-items: center; | ||
padding: 8px; | ||
} | ||
|
||
.comments-list .comment .content p { | ||
display: inline-block; | ||
margin-left: 8px; | ||
} | ||
|
||
.comments-list .comment .info { | ||
color: #AAAAAA; | ||
font-size: 0.85em; | ||
padding: 0 10px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* @flow */ | ||
|
||
import * as React from 'react'; | ||
import ReactTimeAgo from 'react-time-ago'; | ||
|
||
import './Comment.css'; | ||
|
||
import { UserAvatar } from 'core/user' | ||
mathjazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import type { TranslationComment } from 'core/api'; | ||
|
||
|
||
type Props = {| | ||
comment: TranslationComment, | ||
|}; | ||
|
||
|
||
export default function Comment(props: Props) { | ||
const { comment } = props; | ||
|
||
if (!comment) { | ||
return null; | ||
} | ||
|
||
return <li className='comment'> | ||
<UserAvatar | ||
user={ comment.author } | ||
username={ comment.username } | ||
imageUrl={ comment.userGravatarUrlSmall } | ||
/> | ||
<div className='container'> | ||
<div className='content' dir='auto'> | ||
<a | ||
href={ `/contributors/${comment.username}` } | ||
target='_blank' | ||
rel='noopener noreferrer' | ||
onClick={ (e: SyntheticMouseEvent<>) => e.stopPropagation() } | ||
> | ||
{ comment.author } | ||
</a> | ||
<p> | ||
{ comment.content } | ||
</p> | ||
</div> | ||
<div className='info'> | ||
<ReactTimeAgo | ||
dir='ltr' | ||
date={ new Date(comment.dateIso) } | ||
title={ `${comment.createdAt} UTC` } | ||
/> | ||
</div> | ||
</div> | ||
mathjazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</li> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: please use the actual character here:
…
.