Skip to content
Draft
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
28 changes: 16 additions & 12 deletions lang/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -769,12 +769,12 @@
"Dashboard.header.somethingNew": "something new",
"Dashboard.header.userScore": "{points, number} points",
"Dashboard.header.welcomeBack": "Welcome Back, {username}!",
"Editor.id.label": "Edit in iD (web editor)",
"Editor.josm.label": "Edit in JOSM",
"Editor.josmFeatures.label": "Edit just features in JOSM",
"Editor.josmLayer.label": "Edit in new JOSM layer",
"Editor.level0.label": "Edit in Level0",
"Editor.rapid.label": "Edit in Rapid",
"Editor.id.label": "Open in iD Editor",
"Editor.josm.label": "Open in JOSM Editor",
"Editor.josmFeatures.label": "Open just features in JOSM Editor",
"Editor.josmLayer.label": "Open in new JOSM Editor layer",
"Editor.level0.label": "Open in Level0 Editor",
"Editor.rapid.label": "Open in Rapid Editor",
"EnhancedMap.SearchControl.noResults": "No Results",
"EnhancedMap.SearchControl.nominatimQuery.placeholder": "Nominatim Query",
"ErrorModal.title": "Oops!",
Expand Down Expand Up @@ -958,7 +958,7 @@
"KeyMapping.layers.layerMapillary": "Toggle Mapillary Layer",
"KeyMapping.layers.layerOSMData": "Toggle OSM Data Layer",
"KeyMapping.layers.layerTaskFeatures": "Toggle Features Layer",
"KeyMapping.openEditor.editId": "Edit in iD",
"KeyMapping.openEditor.editId": "Open in iD Editor",
"KeyMapping.taskCompletion.alreadyFixed": "Already fixed",
"KeyMapping.taskCompletion.falsePositive": "No / Not an issue",
"KeyMapping.taskCompletion.fixed": "Yes / I fixed it!",
Expand Down Expand Up @@ -1083,8 +1083,8 @@
"Profile.form.customBasemaps.label": "Custom Basemaps",
"Profile.form.defaultBasemap.description": "Select the default basemap to display on the map. Only a default challenge basemap will override the option selected here.",
"Profile.form.defaultBasemap.label": "Default Basemap",
"Profile.form.defaultEditor.description": "Select the default editor that you want to use when fixing tasks. By selecting this option you will be able to skip the editor selection dialog after clicking on edit in a task.",
"Profile.form.defaultEditor.label": "Default Editor",
"Profile.form.defaultEditor.description": "Select the default external editor that you want to use when fixing tasks. This editor opens in a new tab or application and is separate from the embedded Rapid editor shown within MapRoulette. By selecting this option you will be able to skip the editor selection dialog after clicking on edit in a task.",
"Profile.form.defaultEditor.label": "Default External Editor",
"Profile.form.disableTaskConfirm.description": "This will allow the user to bypass the task confirmation modal when a user clicks 'I fixed it!'. Be aware some projects and challenges won't allow this behavior.",
"Profile.form.disableTaskConfirm.label": "Disable Task Confirmation Modal",
"Profile.form.email.description": "If you request emails in your Notification Subscriptions, they will be sent here.\n\nDecide which MapRoulette notifications you would like to receive, along with whether you would like to be sent an email informing you of the notification (either immediately or as a daily digest)",
Expand Down Expand Up @@ -1425,9 +1425,9 @@
"TopUserChallenges.widget.label": "Your Top Challenges",
"User.sort.numOfChallenges": "Score",
"UserEditorSelector.currentEditor.label": "Current Editor:",
"UserEditorSelector.defaultEditor.label": "Set Default Editor:",
"UserEditorSelector.openEditor.label": "Open Editor",
"UserEditorSelector.unsupportedEditor.label": "Open Unsupported Editor:",
"UserEditorSelector.defaultEditor.label": "Set Default External Editor:",
"UserEditorSelector.openEditor.label": "Open External Editor",
"UserEditorSelector.unsupportedEditor.label": "Open Unsupported External Editor:",
"UserProfile.favoriteChallenges.header": "Your Favorite Challenges",
"UserProfile.lockedTasks.header": "Your Locked Tasks",
"UserProfile.savedTasks.header": "Tracked Tasks",
Expand Down Expand Up @@ -1555,9 +1555,13 @@
"Widgets.TaskLocationWidget.controls.showLonLat.label": "Lon/Lat",
"Widgets.TaskLocationWidget.label": "Location",
"Widgets.TaskMapWidget.editMode": "Current Mode:",
"Widgets.TaskMapWidget.externalEditPrompt": "Use the button below to open this task in an external editor.",
"Widgets.TaskMapWidget.rapidDiscardUnsavedChanges": "You have unsaved changes in Rapid which will be discarded. Are you sure you want to proceed?",
"Widgets.TaskMapWidget.rapidFailed": "Widget Failed! Geometries Null!",
"Widgets.TaskMapWidget.reselectTask": "Re-Select Task",
"Widgets.TaskMapWidget.tab.edit": "Rapid Editor",
"Widgets.TaskMapWidget.tab.externalEdit": "External Edit",
"Widgets.TaskMapWidget.tab.view": "Task Map",
"Widgets.TaskMoreOptionsWidget.label": "More Options",
"Widgets.TaskNearbyMap.currentTaskTooltip": "Current Task",
"Widgets.TaskNearbyMap.tooltip.loadMoreTasks.control": "Load More Tasks",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import PropTypes from "prop-types";
import { Component } from "react";
import { FormattedMessage } from "react-intl";
import { TaskStatus } from "../../../../../services/Task/TaskStatus/TaskStatus";
import UserEditorSelector from "../../../../UserEditorSelector/UserEditorSelector";
import TaskAlreadyFixedControl from "../TaskAlreadyFixedControl/TaskAlreadyFixedControl";
import TaskFalsePositiveControl from "../TaskFalsePositiveControl/TaskFalsePositiveControl";
import TaskFixedControl from "../TaskFixedControl/TaskFixedControl";
Expand Down Expand Up @@ -49,7 +48,6 @@ export default class TaskCompletionStep extends Component {
</div>
</div>
)}
<UserEditorSelector {...this.props} className="mr-mb-2" />
<div className="breadcrumb mr-w-full mr-flex mr-flex-wrap mr-m-auto mr-items-center">
{this.props.needsRevised && (
<div className="mr-mt-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { describe, expect, it } from "vitest";
import TaskCompletionStep from "./TaskCompletionStep";

describe("TaskCompletionStep", () => {
it("renders task completion step 1 with required props", () => {
const { getByText } = global.withProvider(
it("renders task completion step with required props", () => {
const { container } = global.withProvider(
<TaskCompletionStep
task={{}}
pickEditor={() => null}
Expand All @@ -15,13 +15,12 @@ describe("TaskCompletionStep", () => {
deactivateKeyboardShortcutGroup={() => null}
/>,
);
const text = getByText("Open Editor");
expect(text).toBeInTheDocument();
expect(container.querySelector(".breadcrumb")).toBeInTheDocument();
});

it("shows Edit button if allowedProgressions includes 1", () => {
it("shows fixed button if allowedProgressions includes fixed status", () => {
const allowedProgressions = new Map();
allowedProgressions.set(1);
allowedProgressions.set(1, true);

const { getByText } = global.withProvider(
<TaskCompletionStep
Expand All @@ -35,7 +34,7 @@ describe("TaskCompletionStep", () => {
deactivateKeyboardShortcutGroup={() => null}
/>,
);
const text = getByText("Open Editor");
const text = getByText("I fixed it!");
expect(text).toBeInTheDocument();
});
});
6 changes: 3 additions & 3 deletions src/components/UserEditorSelector/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ export default defineMessages({

editLabel: {
id: "UserEditorSelector.openEditor.label",
defaultMessage: "Open Editor",
defaultMessage: "Open External Editor",
},

defaultEditor: {
id: "UserEditorSelector.defaultEditor.label",
defaultMessage: "Set Default Editor:",
defaultMessage: "Set Default External Editor:",
},

unsupportedEditor: {
id: "UserEditorSelector.unsupportedEditor.label",
defaultMessage: "Open Unsupported Editor:",
defaultMessage: "Open Unsupported External Editor:",
},
});
20 changes: 20 additions & 0 deletions src/components/Widgets/TaskMapWidget/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,24 @@ export default defineMessages({
id: "Widgets.TaskMapWidget.reselectTask",
defaultMessage: "Re-Select Task",
},

viewTab: {
id: "Widgets.TaskMapWidget.tab.view",
defaultMessage: "Task Map",
},

editTab: {
id: "Widgets.TaskMapWidget.tab.edit",
defaultMessage: "Rapid Editor",
},

externalEditTab: {
id: "Widgets.TaskMapWidget.tab.externalEdit",
defaultMessage: "External Edit",
},

externalEditPrompt: {
id: "Widgets.TaskMapWidget.externalEditPrompt",
defaultMessage: "Use the button below to open this task in an external editor.",
},
});
121 changes: 94 additions & 27 deletions src/components/Widgets/TaskMapWidget/TaskMapWidget.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Component } from "react";
import { FormattedMessage } from "react-intl";
import { replacePropertyTags } from "../../../hooks/UsePropertyReplacement/UsePropertyReplacement";
import AsCooperativeWork from "../../../interactions/Task/AsCooperativeWork";
import { Editor } from "../../../services/Editor/Editor";
import { OPEN_STREET_MAP } from "../../../services/VisibleLayer/LayerSources";
import { WidgetDataTarget, registerWidgetType } from "../../../services/Widget/Widget";
import MapPane from "../../EnhancedMap/MapPane/MapPane";
import WithEditor from "../../HOCs/WithEditor/WithEditor";
import WithKeyboardShortcuts from "../../HOCs/WithKeyboardShortcuts/WithKeyboardShortcuts";
import QuickWidget from "../../QuickWidget/QuickWidget";
import TaskMap from "../../TaskPane/TaskMap/TaskMap";
import UserEditorSelector from "../../UserEditorSelector/UserEditorSelector";
import messages from "./Messages";
import EditSwitch from "./RapidEditor/EditSwitch";
import RapidEditor from "./RapidEditor/RapidEditor";

const TAB_VIEW = "view";
const TAB_EDIT = "edit";

const descriptor = {
widgetKey: "TaskMapWidget",
label: messages.label,
Expand All @@ -20,24 +27,68 @@ const descriptor = {
defaultHeight: 19,
};

const getActiveTab = (props, disableRapid) => {
if (!props.getUserAppSetting) return TAB_VIEW;

const mapTab = props.getUserAppSetting(props.user, "mapTab");
if (mapTab) {
// If editing is disabled, force view tab
if (disableRapid && mapTab !== TAB_VIEW) return TAB_VIEW;
return mapTab;
}

// Backward compatibility: migrate from isEditMode boolean
const isEditMode = props.getUserAppSetting(props.user, "isEditMode");
if (isEditMode && !disableRapid) return TAB_EDIT;
return TAB_VIEW;
};

export default class TaskMapWidget extends Component {
componentWillUnmount = () => {
this.props.resumeKeyboardShortcuts();
};

componentDidUpdate(prevProps) {
const prevEditMode = prevProps.getUserAppSetting
? prevProps.getUserAppSetting(prevProps.user, "isEditMode")
: false;
const currentEditMode = this.props.getUserAppSetting
? this.props.getUserAppSetting(this.props.user, "isEditMode")
: false;

if (currentEditMode !== prevEditMode) {
currentEditMode ? this.props.pauseKeyboardShortcuts() : this.props.resumeKeyboardShortcuts();
const prevTab = getActiveTab(prevProps, false);
const currentTab = getActiveTab(this.props, false);

if (currentTab !== prevTab) {
currentTab === TAB_EDIT
? this.props.pauseKeyboardShortcuts()
: this.props.resumeKeyboardShortcuts();
}
}

setTab = (tab) => {
if (this.props.updateUserAppSetting) {
this.props.updateUserAppSetting(this.props.user.id, { mapTab: tab });
}
};

pickEditor = ({ value }) => {
const { task, taskFeatureProperties } = this.props;
const comment = task.parent?.checkinComment;
const replacedComment = replacePropertyTags(comment, taskFeatureProperties, false);

this.props.editTask(
value,
task,
this.props.mapBounds,
{
imagery: this.props.source?.id !== OPEN_STREET_MAP ? this.props.source : undefined,
photoOverlay: this.props.showMapillaryLayer ? "mapillary" : null,
},
this.props.taskBundle,
replacedComment,
);
};

allowedEditors = () => {
return AsCooperativeWork(this.props.task).isChangeFileType()
? [Editor.josmLayer, Editor.josm]
: null;
};

render() {
const cooperative =
AsCooperativeWork(this.props.task).isTagType() || this.props.task.cooperativeWork;
Expand All @@ -52,11 +103,7 @@ export default class TaskMapWidget extends Component {
!isReviewing &&
!this.props.asMetaReview);

const editMode = disableRapid
? false
: this.props.getUserAppSetting
? this.props.getUserAppSetting(this.props.user, "isEditMode")
: false;
const activeTab = getActiveTab(this.props, disableRapid);

if (!this.props.task.geometries.features) {
return (
Expand All @@ -72,25 +119,45 @@ export default class TaskMapWidget extends Component {
this.props.currentConfiguration?.type === "leftPanel" ||
this.props.currentConfiguration?.type === "rightPanel";

const tabs = [
{ key: TAB_VIEW, label: messages.viewTab, disabled: false },
{ key: TAB_EDIT, label: messages.editTab, disabled: disableRapid },
];

return (
<QuickWidget {...this.props} className="task-map-widget" noMain permanent>
<div
className="mr-mt-2"
style={{ height: altWorkspaceType ? "calc(100vh - 270px)" : "calc(100% - 3rem)" }}
>
{this.props.getUserAppSetting ? (
<>
<div className="mr-flex mr-items-center ">
<div className="mr-text-yellow mr-mr-1 mr-mt-1 mr-mb-2">
<FormattedMessage {...messages.editMode} />
</div>
<div className="mr-mt-1 mr-mb-2">
<EditSwitch {...this.props} disableRapid={disableRapid} editMode={editMode} />
</div>
</div>
</>
<div className="mr-flex mr-items-center mr-mb-2">
{tabs.map((tab) => (
<button
key={tab.key}
className={`mr-px-3 mr-py-1 mr-text-sm mr-font-medium mr-border-b-2 mr-mr-2 ${
activeTab === tab.key
? "mr-border-green-light mr-text-green-light"
: tab.disabled
? "mr-border-transparent mr-text-grey mr-cursor-not-allowed mr-opacity-50"
: "mr-border-transparent mr-text-white hover:mr-text-green-lighter hover:mr-border-green-lighter mr-cursor-pointer"
}`}
onClick={() => !tab.disabled && this.setTab(tab.key)}
disabled={tab.disabled}
>
<FormattedMessage {...tab.label} />
</button>
))}
{!disableRapid && (
<UserEditorSelector
{...this.props}
pickEditor={this.pickEditor}
allowedEditors={this.allowedEditors()}
/>
)}
</div>
) : null}
{editMode ? (
{activeTab === TAB_EDIT ? (
<RapidEditor
token={this.props.user.osmProfile.requestToken}
task={this.props.task}
Expand All @@ -108,4 +175,4 @@ export default class TaskMapWidget extends Component {
}
}

registerWidgetType(WithKeyboardShortcuts(TaskMapWidget), descriptor);
registerWidgetType(WithEditor(WithKeyboardShortcuts(TaskMapWidget)), descriptor);
4 changes: 2 additions & 2 deletions src/pages/Profile/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export default defineMessages({

defaultEditorLabel: {
id: "Profile.form.defaultEditor.label",
defaultMessage: "Default Editor",
defaultMessage: "Default External Editor",
},

defaultEditorDescription: {
id: "Profile.form.defaultEditor.description",
defaultMessage:
"Select the default editor that you want to use when fixing tasks. By selecting this option you will be able to skip the editor selection dialog after clicking on edit in a task.",
"Select the default external editor that you want to use when fixing tasks. This editor opens in a new tab or application and is separate from the embedded Rapid editor shown within MapRoulette. By selecting this option you will be able to skip the editor selection dialog after clicking on edit in a task.",
},

defaultBasemapLabel: {
Expand Down
12 changes: 6 additions & 6 deletions src/services/Editor/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,26 @@ export default defineMessages({
},
id: {
id: "Editor.id.label",
defaultMessage: "Edit in iD (web editor)",
defaultMessage: "Open in iD Editor",
},
josm: {
id: "Editor.josm.label",
defaultMessage: "Edit in JOSM",
defaultMessage: "Open in JOSM Editor",
},
josmLayer: {
id: "Editor.josmLayer.label",
defaultMessage: "Edit in new JOSM layer",
defaultMessage: "Open in new JOSM Editor layer",
},
josmFeatures: {
id: "Editor.josmFeatures.label",
defaultMessage: "Edit just features in JOSM",
defaultMessage: "Open just features in JOSM Editor",
},
level0: {
id: "Editor.level0.label",
defaultMessage: "Edit in Level0",
defaultMessage: "Open in Level0 Editor",
},
rapid: {
id: "Editor.rapid.label",
defaultMessage: "Edit in Rapid",
defaultMessage: "Open in Rapid Editor",
},
});
Loading