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

Fix Bug 1603610 - Enable users to leave comments on translations #1524

Merged
merged 58 commits into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
af6fcef
Initial setup of model for translation comments
abowler2 Dec 13, 2019
8cc1297
Made updates to and migrated Comment model
abowler2 Dec 13, 2019
1d3885a
Send comment info to the frontend
abowler2 Dec 15, 2019
7f72c2b
Refactoring
abowler2 Dec 17, 2019
7cb311a
Initial steps to render comments on frontend
abowler2 Dec 18, 2019
22e3cce
Requested revisions
abowler2 Dec 19, 2019
0efd8f0
Serialize comments and initial work on Comments component
abowler2 Dec 20, 2019
7cdbc07
Refactored to add Comment and AddComment components
abowler2 Dec 20, 2019
476afcc
Refactoring of comment components
abowler2 Dec 20, 2019
ebf1d67
Requested refactoring
abowler2 Dec 20, 2019
3df52ab
Fixed linting errors
abowler2 Dec 20, 2019
1fcc776
Refactoring and requested changes
abowler2 Dec 23, 2019
a6c3939
Added function to rename keys in getHistory
abowler2 Dec 24, 2019
ab6cf20
Added initial tests for Comments and CommentsList
abowler2 Dec 28, 2019
7f62e1e
Revision of camelCase conversion and refactoring
abowler2 Dec 30, 2019
ac452f5
Added typing to camelCase argument
abowler2 Dec 30, 2019
b9fa253
Initial styling and requested revisions
abowler2 Dec 30, 2019
31d9559
Corrected localization and refactored
abowler2 Dec 31, 2019
bf863cc
Readded code lost during rebase
abowler2 Dec 31, 2019
917df09
Initial set up for delete and share buttons
abowler2 Jan 1, 2020
9c3c2af
Changed classnames for comment and comment list and refactored
abowler2 Jan 3, 2020
70aaf6e
Added ability to verify delete comment permissions and function to de…
abowler2 Jan 6, 2020
c6fa2a7
Added tests to Comment test file
abowler2 Jan 7, 2020
9deeb20
Updated test
abowler2 Jan 7, 2020
0cc6b5d
Deleted unneeded migrations
abowler2 Jan 7, 2020
3ac721e
Added comment_deleted to action log
abowler2 Jan 7, 2020
2c2923a
Initial work for Add Comment
abowler2 Jan 8, 2020
cb62abe
Renamed canReview, added api/comment, added translation to del commen…
abowler2 Jan 8, 2020
654b5d5
Removed unneeded check in action log, added initial add comment function
abowler2 Jan 9, 2020
528edef
Corrected functions to camelCase
abowler2 Jan 9, 2020
60898f2
Minor refactor for typing
abowler2 Jan 9, 2020
b3ea3b0
Added ability to save comment to DB
abowler2 Jan 10, 2020
902de9d
Initial work to set button for toggling comments panel
abowler2 Jan 11, 2020
8762a10
Fixed broken tranlation test
abowler2 Jan 11, 2020
2e34f21
Added check for null value and AddComment test
abowler2 Jan 13, 2020
bc7656b
Added localized title and refactored
abowler2 Jan 13, 2020
eb2f579
Requested edits
abowler2 Jan 14, 2020
3383171
Made changes to localization for comment button
abowler2 Jan 14, 2020
cc254a3
Requested changes, refactored title in UserAvatar
abowler2 Jan 17, 2020
71b9288
Removed the Delete Comment functionality
abowler2 Jan 17, 2020
0260b78
Added new test for AddComment
abowler2 Jan 17, 2020
633ad4a
Break comment css into individual files
abowler2 Jan 18, 2020
6d5ad27
Changed input to textarea for AddComment
abowler2 Jan 19, 2020
b988f38
Fixed spacing issues
abowler2 Jan 20, 2020
95c92f6
Updated comments button settings, fixed collapsing comments, fixed te…
abowler2 Jan 21, 2020
4e2e1c4
Hid comment button when no comments and not authenticated
abowler2 Jan 21, 2020
62cca2b
Updated permissions for comments
abowler2 Jan 21, 2020
58e5975
Made change to allow tests to pass
abowler2 Jan 21, 2020
e1bf8e6
Changed the changes and fixed the test
abowler2 Jan 21, 2020
95687ee
updated test
abowler2 Jan 21, 2020
8adcef8
Applied CSS patch
abowler2 Jan 22, 2020
9e56237
Additional CSS changes
abowler2 Jan 22, 2020
a7a13a7
Fixed error with className
abowler2 Jan 22, 2020
ad32c94
Added font-weight to comments
abowler2 Jan 22, 2020
9230e0c
Fixed localization and syntax issues. Made needed migrations
abowler2 Jan 23, 2020
137eeec
Refactored comment button
abowler2 Jan 23, 2020
1bccc01
Requested changes to Comment toggle
abowler2 Jan 23, 2020
6ad244f
Disabled comments until team comments complete
abowler2 Jan 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/public/static/locale/en-US/translate.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Copy link
Collaborator

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: .



## Editor Menu
## Allows contributors to modify or propose a translation

Expand Down Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down
32 changes: 32 additions & 0 deletions frontend/src/core/api/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ export default class APIBase {
return new URL(url, window.location.origin);
}

toCamelCase = (s: string) => {
return s.replace(/([-_][a-z])/ig, ($1) => {
return $1.toUpperCase()
.replace('-', '')
.replace('_', '');
});
}

isObject = function (obj: any) {
return obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function';
}

async fetch(
url: string,
method: string,
Expand Down Expand Up @@ -87,4 +99,24 @@ export default class APIBase {
return {};
}
}

keysToCamelCase(results: any) {
if (this.isObject(results)) {
const newObj: any = {};

Object.keys(results)
.forEach((key) => {
newObj[this.toCamelCase(key)] = this.keysToCamelCase(results[key]);
});

return newObj;
}
else if (Array.isArray(results)) {
return results.map((i) => {
return this.keysToCamelCase(i);
});
}
abowler2 marked this conversation as resolved.
Show resolved Hide resolved

return results;
}
}
19 changes: 19 additions & 0 deletions frontend/src/core/api/comment.js
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);
}
}
4 changes: 3 additions & 1 deletion frontend/src/core/api/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ export default class EntityAPI extends APIBase {
const headers = new Headers();
headers.append('X-Requested-With', 'XMLHttpRequest');

return await this.fetch('/get-history/', 'GET', payload, headers);
const results = await this.fetch('/get-history/', 'GET', payload, headers);

return this.keysToCamelCase(results);
}

async getOtherLocales(
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/core/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import ProjectAPI from './project';
import ResourceAPI from './resource';
import TranslationAPI from './translation';
import UserAPI from './user';
import CommentAPI from './comment';


export type {
Entities,
Entity,
EntityTranslation,
TranslationComment,
MachineryTranslation,
OtherLocaleTranslations,
OtherLocaleTranslation,
Expand All @@ -23,6 +25,7 @@ export type {

export default {
entity: new EntityAPI(),
comment: new CommentAPI(),
filter: new FilterAPI(),
locale: new LocaleAPI(),
l10n: new L10nAPI(),
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/core/api/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ export type EntityTranslation = {|
|};


/**
* Comments pertaining to a translation.
*/
export type TranslationComment = {|
+author: string,
+username: string,
+userGravatarUrlSmall: string,
+createdAt: string,
+dateIso: string,
+content: string,
+id: number,
|};


/**
* String that needs to be translated, along with its current metadata,
* and its currently accepted translations.
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/core/comments/components/AddComment.css
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;
}
76 changes: 76 additions & 0 deletions frontend/src/core/comments/components/AddComment.js
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'}` }
Copy link
Collaborator

Choose a reason for hiding this comment

The 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: (you can just copy and paste it ;) ).

Copy link
Collaborator

Choose a reason for hiding this comment

The 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 en-US FTL file, not what is in the code. That's there only to help developers understand what the content is without having to go look into the .ftl files, but that content will never be shown to users. We should aim to have content in code always in sync with content in the en-US reference FTL files, even if that's a bit painful to do.

ref={ commentInput }
onKeyDown={ onEnterSubmit }
/>
</Localized>
</form>
</div>
}
29 changes: 29 additions & 0 deletions frontend/src/core/comments/components/AddComment.test.js
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',
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
});
});
24 changes: 24 additions & 0 deletions frontend/src/core/comments/components/Comment.css
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;
}
54 changes: 54 additions & 0 deletions frontend/src/core/comments/components/Comment.js
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>
}