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

Autofill OSM usernames in comment box based on the tasks contributors #4704

Merged
merged 3 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion frontend/src/components/comments/commentInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DROPZONE_SETTINGS } from '../../config';
export const CommentInputField = ({
comment,
setComment,
contributors,
enableHashtagPaste = false,
autoFocus,
}: Object) => {
Expand All @@ -28,6 +29,7 @@ export const CommentInputField = ({
<UserFetchTextarea
inputProps={getInputProps}
value={comment}
contributors={contributors}
setValueFn={(e) => setComment(e.target.value)}
autoFocus={autoFocus}
/>
Expand All @@ -44,10 +46,11 @@ export const CommentInputField = ({
);
};

export const UserFetchTextarea = ({ value, setValueFn, inputProps, autoFocus }) => {
export const UserFetchTextarea = ({ value, setValueFn, inputProps, contributors, autoFocus }) => {
const token = useSelector((state) => state.auth.get('token'));
const fetchUsers = async (user) => {
try {
if (!user) return contributors.map((u) => ({ name: u }));
const res = await fetchLocalJSONAPI(`users/queries/filter/${user}/`, token);
return res.usernames.map((u) => ({ name: u }));
} catch (e) {
Expand All @@ -63,6 +66,7 @@ export const UserFetchTextarea = ({ value, setValueFn, inputProps, autoFocus })
listClassName="list ma0 pa0 ba b--grey-light bg-blue-grey overflow-y-scroll base-font f5 relative z-5"
listStyle={{ maxHeight: '16rem' }}
onChange={setValueFn}
minChar={0}
className="w-100 f5 pa2"
style={{ fontSize: '1rem' }}
loadingComponent={() => <span></span>}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/components/taskSelection/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Button } from '../button';
import Portal from '../portal';
import { SidebarIcon } from '../svgIcons';
import { openEditor, getTaskGpxUrl, formatImageryUrl } from '../../utils/openEditor';
import { getTaskContributors } from '../../utils/getTaskContributors';
import { TaskHistory } from './taskActivity';
import { ChangesetCommentTags } from './changesetComment';
import { useSetProjectPageTitleTag } from '../../hooks/UseMetaTags';
Expand Down Expand Up @@ -70,6 +71,11 @@ export function TaskMapAction({ project, projectIsReady, tasks, activeTasks, act
project.projectId && tasksIds && tasksIds.length === 1,
);

const contributors =
taskHistory && taskHistory.taskHistory
? getTaskContributors(taskHistory.taskHistory, userDetails.username)
: [];

const readTaskComments = useReadTaskComments(taskHistory);
const disableBadImagery = useDisableBadImagery(taskHistory);

Expand Down Expand Up @@ -240,6 +246,7 @@ export function TaskMapAction({ project, projectIsReady, tasks, activeTasks, act
disableBadImagery={
userDetails.mappingLevel !== 'ADVANCED' && disableBadImagery
}
contributors={contributors}
historyTabSwitch={historyTabSwitch}
taskInstructions={
activeTasks && activeTasks.length === 1
Expand All @@ -263,6 +270,7 @@ export function TaskMapAction({ project, projectIsReady, tasks, activeTasks, act
: null
}
disabled={disabled}
contributors={contributors}
validationComments={validationComments}
setValidationComments={setValidationComments}
validationStatus={validationStatus}
Expand Down
30 changes: 29 additions & 1 deletion frontend/src/components/taskSelection/actionSidebars.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { navigate } from '@reach/router';
import Popup from 'reactjs-popup';
Expand All @@ -22,6 +22,7 @@ import {
} from '../svgIcons';
import { getEditors } from '../../utils/editorsList';
import { htmlFromMarkdown } from '../../utils/htmlFromMarkdown';
import { getTaskContributors } from '../../utils/getTaskContributors';
import { pushToLocalJSONAPI, fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { CommentInputField } from '../comments/commentInput';
import { useFetchLockedTasks, useClearLockedTasks } from '../../hooks/UseLockedTasks';
Expand All @@ -35,6 +36,7 @@ export function CompletionTabForMapping({
historyTabSwitch,
taskInstructions,
disabled,
contributors,
taskComment,
setTaskComment,
selectedStatus,
Expand Down Expand Up @@ -217,6 +219,7 @@ export function CompletionTabForMapping({
<CommentInputField
comment={taskComment}
setComment={setTaskComment}
contributors={contributors}
enableHashtagPaste={true}
/>
</p>
Expand Down Expand Up @@ -263,6 +266,7 @@ export function CompletionTabForValidation({
tasksIds,
taskInstructions,
disabled,
contributors,
validationStatus,
setValidationStatus,
validationComments,
Expand Down Expand Up @@ -358,6 +362,8 @@ export function CompletionTabForValidation({
<TaskValidationSelector
key={id}
id={id}
projectId={project.projectId}
contributors={contributors}
currentStatus={validationStatus[id]}
updateStatus={updateStatus}
comment={validationComments[id]}
Expand Down Expand Up @@ -394,16 +400,37 @@ export function CompletionTabForValidation({

const TaskValidationSelector = ({
id,
projectId,
currentStatus,
comment,
updateComment,
contributors,
updateStatus,
copyCommentToTasks,
isValidatingMultipleTasks,
}) => {
const userDetails = useSelector((state) => state.auth.get('userDetails'));
const [showCommentInput, setShowCommentInput] = useState(false);
const [enableCopy, setEnableCopy] = useState(false);
const setComment = (newComment) => updateComment(id, newComment);
const [contributorsList, setContributorsList] = useState([]);

// the contributors is filled only on the case of single task validation,
// so we need to fetch the task history in the case of multiple task validation
useEffect(() => {
if (showCommentInput && isValidatingMultipleTasks && !contributors.length) {
fetchLocalJSONAPI(`projects/${projectId}/tasks/${id}/`).then((response) =>
setContributorsList(getTaskContributors(response.taskHistory, userDetails.username)),
);
}
}, [
isValidatingMultipleTasks,
showCommentInput,
contributors,
id,
projectId,
userDetails.username,
]);

return (
<div className="cf w-100 db pt1 pv2 blue-dark">
Expand Down Expand Up @@ -453,6 +480,7 @@ const TaskValidationSelector = ({
<CommentInputField
comment={comment}
setComment={setComment}
contributors={contributors.length ? contributors : contributorsList}
enableHashtagPaste={false}
autoFocus={true}
/>
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/components/taskSelection/taskActivity.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import useGetContributors from '../../hooks/UseGetContributors';
import { RelativeTimeWithUnit } from '../../utils/formattedRelativeTime';
import { formatOSMChaLink } from '../../utils/osmchaLink';
import { htmlFromMarkdown, formatUserNamesToLink } from '../../utils/htmlFromMarkdown';
import { getTaskContributors } from '../../utils/getTaskContributors';
import { getIdUrl, sendJosmCommands } from '../../utils/openEditor';
import { formatOverpassLink } from '../../utils/overpassLink';
import { pushToLocalJSONAPI, fetchLocalJSONAPI } from '../../network/genericJSONRequest';
Expand All @@ -22,7 +23,7 @@ import { Dropdown } from '../dropdown';
import { CommentInputField } from '../comments/commentInput';
import { CheckBoxInput } from '../formInputs';

const PostComment = ({ projectId, taskId, setCommentPayload }) => {
const PostComment = ({ projectId, taskId, contributors, setCommentPayload }) => {
const token = useSelector((state) => state.auth.get('token'));
const [comment, setComment] = useState('');

Expand All @@ -49,7 +50,12 @@ const PostComment = ({ projectId, taskId, setCommentPayload }) => {
<CurrentUserAvatar className="h2 w2 fr mr2 br-100" />
</div>
<div className="fl w-70 f6">
<CommentInputField comment={comment} setComment={setComment} enableHashtagPaste={true} />
<CommentInputField
comment={comment}
setComment={setComment}
enableHashtagPaste={true}
contributors={contributors}
/>
</div>
<div className="w-20 fr pt3 tr">
<Button onClick={() => saveComment()} className="bg-red white f6">
Expand Down Expand Up @@ -266,6 +272,7 @@ export const TaskActivity = ({
userCanValidate,
}: Object) => {
const token = useSelector((state) => state.auth.get('token'));
const userDetails = useSelector((state) => state.auth.get('userDetails'));
// use it to hide the reset task action button
const [resetSuccess, setResetSuccess] = useState(false);
const [commentPayload, setCommentPayload] = useState(null);
Expand Down Expand Up @@ -351,6 +358,11 @@ export const TaskActivity = ({
projectId={project.projectId}
taskId={taskId}
setCommentPayload={setCommentPayload}
contributors={
commentPayload && commentPayload.taskHistory
? getTaskContributors(commentPayload.taskHistory, userDetails.username)
: []
}
/>
</div>
);
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/utils/getTaskContributors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getTaskContributors = (taskHistory, usernameToExclude) =>
taskHistory.reduce((acc, history) => {
if (!acc.includes(history.actionBy) && history.actionBy !== usernameToExclude) {
acc.push(history.actionBy);
}
return acc.sort();
}, []);
15 changes: 15 additions & 0 deletions frontend/src/utils/tests/getTaskContributors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getTaskContributors } from '../getTaskContributors';
import { history, revertedBadImagery } from '../../network/tests/mockData/taskHistory';

test('getTaskContributors returns an ordered result & excludes the username (2nd arg)', () => {
expect(getTaskContributors(history.taskHistory, 'geochica')).toEqual(['test_user', 'user_123']);
expect(getTaskContributors(revertedBadImagery.taskHistory, 'geochica')).toEqual([
'test_user',
'user_1',
'user_11',
]);
expect(getTaskContributors(revertedBadImagery.taskHistory, 'user_1')).toEqual([
'test_user',
'user_11',
]);
});