From c8929405c769770452a4ee140907abcaf3b94748 Mon Sep 17 00:00:00 2001 From: Kelli Rotstan Date: Fri, 6 Dec 2019 08:40:50 -0800 Subject: [PATCH 01/38] Remove extra fetchTaskClusters call in admin challenge view FetchTaskClusters in admin challenge view was being called twice. First with no bounds to fetch initial markers, then when the map was rendered with the updated bounding box. This would then allow tasksInBounds (table tasks) to be fetched as it had bounds. Changed to remove second fetchTaskClusters and have tasksInBounds fetch all tasks using global bounds. Thereby eliminating redundant fetchTaskClusters. --- .../WithFilterCriteria/WithFilterCriteria.js | 19 +++++++++++++++++-- .../TaskClusterMap/TaskClusterMap.js | 8 ++++++++ src/services/MapBounds/MapBounds.js | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/components/HOCs/WithFilterCriteria/WithFilterCriteria.js b/src/components/HOCs/WithFilterCriteria/WithFilterCriteria.js index 9b9d63036..2051a4983 100644 --- a/src/components/HOCs/WithFilterCriteria/WithFilterCriteria.js +++ b/src/components/HOCs/WithFilterCriteria/WithFilterCriteria.js @@ -6,7 +6,7 @@ import _keys from 'lodash/keys' import _pickBy from 'lodash/pickBy' import _omit from 'lodash/omit' import _sortBy from 'lodash/sortBy' -import { fromLatLngBounds } from '../../../services/MapBounds/MapBounds' +import { fromLatLngBounds, GLOBAL_MAPBOUNDS } from '../../../services/MapBounds/MapBounds' import { fetchPropertyKeys } from '../../../services/Challenge/Challenge' const DEFAULT_PAGE_SIZE = 20 @@ -142,8 +142,19 @@ export const WithFilterCriteria = function(WrappedComponent) { const challengeId = _get(this.props, 'challenge.id') || this.props.challengeId this.setState({loading: true}) + const criteria = _cloneDeep(this.state.criteria) + + // If we don't have bounds yet, we still want results so let's fetch all + // tasks globally for this challenge. + if (!criteria.boundingBox) { + if (this.props.skipInitialFetch || !challengeId) { + return + } + criteria.boundingBox = GLOBAL_MAPBOUNDS + } + this.props.augmentClusteredTasks(challengeId, false, - this.state.criteria, + criteria, this.state.criteria.pageSize, false).then((results) => { this.setState({loading: false}) @@ -171,6 +182,10 @@ export const WithFilterCriteria = function(WrappedComponent) { this.refreshTasks() } } + else if (_get(prevProps, 'challenge.id') !== _get(this.props, 'challenge.id') || + this.props.challengeId !== prevProps.challengeId) { + this.refreshTasks() + } } render() { diff --git a/src/components/TaskClusterMap/TaskClusterMap.js b/src/components/TaskClusterMap/TaskClusterMap.js index b2bbef50d..20fc8a27b 100644 --- a/src/components/TaskClusterMap/TaskClusterMap.js +++ b/src/components/TaskClusterMap/TaskClusterMap.js @@ -70,6 +70,7 @@ export class TaskClusterMap extends Component { currentSize = null currentZoom = MIN_ZOOM timerHandle = null + skipNextBoundsUpdate = false state = { mapMarkers: null, @@ -163,6 +164,11 @@ export class TaskClusterMap extends Component { this.currentBounds = toLatLngBounds(bounds) this.currentZoom = zoom this.currentSize = mapSize + + if (this.skipNextBoundsUpdate) { + this.skipNextBoundsUpdate = false + return + } this.debouncedUpdateBounds(bounds, zoom) } @@ -354,6 +360,8 @@ export class TaskClusterMap extends Component { ) )) ) + // We've calculated the bounds so we don't need to do the next bounds update + this.skipNextBoundsUpdate = true } else if (this.props.initialBounds) { this.currentBounds = this.props.initialBounds diff --git a/src/services/MapBounds/MapBounds.js b/src/services/MapBounds/MapBounds.js index 251f845b6..e0e809827 100644 --- a/src/services/MapBounds/MapBounds.js +++ b/src/services/MapBounds/MapBounds.js @@ -14,6 +14,8 @@ export const DEFAULT_MAP_BOUNDS = [ 22.51255695405145, // north ] +export const GLOBAL_MAPBOUNDS = [-180, -90, 180, 90] + // utility functions /** From 02bd3d8804653cc14bfad00db4e6290d339e2e7d Mon Sep 17 00:00:00 2001 From: Kelli Rotstan Date: Tue, 21 Jan 2020 14:32:46 -0800 Subject: [PATCH 02/38] Restore reviewer stats in user metrics --- src/pages/Metrics/Metrics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Metrics/Metrics.js b/src/pages/Metrics/Metrics.js index 0ad3b2e44..2cb670f68 100644 --- a/src/pages/Metrics/Metrics.js +++ b/src/pages/Metrics/Metrics.js @@ -140,7 +140,7 @@ class Metrics extends Component { - {this.props.reviewerMetrics && false && + {this.props.reviewerMetrics &&
Date: Sat, 18 Jan 2020 08:32:57 -0800 Subject: [PATCH 03/38] General color and styling updates * Adjust basic theme colors, such as green, blue, gradients, and card backgrounds * Switch user dashboard from light mode to dark mode * Basic styling updates to forms app wide * Split up User Settings into two forms (general settings and notifications) to make layout more flexible * Make admin project-listing icon styling consistent with admin challenge-listing icon styling * Restyle various modals and dialogs * Change TaskAnalysisTableHeader to use the ConfirmAction component instead of its own custom confirmation modal * Replace remaining bulma-styled buttons with updated styling --- .../ActivityTimeline/ActivityTimeline.scss | 23 +-- .../UserActivityTimelineWidget.js | 3 +- src/components/AdminPane/AdminPane.scss | 9 +- .../Manage/BurndownChart/BurndownChart.js | 2 +- .../Manage/ChallengeCard/ChallengeCard.js | 11 +- .../ChallengeDashboard/ChallengeDashboard.js | 12 +- .../Manage/ChallengeList/ChallengeList.js | 24 ++-- .../Manage/CompletionRadar/CompletionRadar.js | 2 +- .../Manage/EditProject/EditProject.js | 22 +-- .../Manage/InspectTask/InspectTask.js | 2 +- .../EditChallenge/EditChallenge.js | 6 +- .../Manage/ManageTasks/EditTask/EditTask.js | 22 +-- .../Manage/ProjectCard/ProjectCard.js | 28 +++- .../ProjectDashboard/ProjectDashboard.js | 12 +- .../Manage/ProjectOverview/ProjectOverview.js | 2 +- .../ProjectsDashboard/ProjectsDashboard.js | 4 +- .../RebuildTasksControl.js | 93 ++++++------ .../RebuildTasksControl.scss | 60 -------- .../Manage/StepNavigation/StepNavigation.js | 70 ++++++--- .../AssociatedChallengeList.js | 15 +- .../VirtualProjects/ManageChallengeList.js | 6 +- .../Widgets/CommentsWidget/CommentsWidget.js | 16 ++- .../ProjectListWidget/ProjectListWidget.js | 11 +- .../RJSFFormFieldAdapter.js | 6 +- src/components/Bulma/Steps.scss | 23 +++ .../ChallengeFilterSubnav.js | 2 +- .../ChallengeProgress/ChallengeProgress.js | 4 +- src/components/ConfirmAction/ConfirmAction.js | 114 +++++++++------ src/components/ConfirmAction/Messages.js | 4 +- src/components/ErrorModal/ErrorModal.js | 41 ++++-- src/components/ErrorModal/ErrorModal.scss | 47 ------- src/components/ErrorModal/Messages.js | 11 ++ src/components/Modal/Modal.js | 3 +- .../SavedChallenges/SavedChallengesWidget.js | 2 +- src/components/SavedTasks/SavedTasksWidget.js | 2 +- src/components/SearchBox/SearchBox.js | 4 +- .../TaskAnalysisTable/TaskAnalysisTable.js | 2 +- .../TaskAnalysisTableHeader.js | 67 +++------ .../TaskClusterMap/ZoomInMessage.js | 2 +- src/components/TaskFilters/FilterDropdown.js | 2 +- .../TopUserChallengesWidget.js | 14 +- src/lang/en-US.json | 10 +- src/pages/Dashboard/Dashboard.js | 4 +- src/pages/Leaderboard/CardLeaderboard.js | 2 +- src/pages/Leaderboard/Leaderboard.js | 2 +- src/pages/Profile/ApiKey.js | 33 ++--- src/pages/Profile/Messages.js | 12 +- src/pages/Profile/Profile.js | 32 ++--- .../NotificationSettingsSchema.js | 74 ++++++++++ .../Profile/UserSettings/UserSettings.js | 133 ++++++++++++------ .../UserSettings/UserSettingsSchema.js | 35 ----- src/services/Task/TaskStatus/TaskStatus.js | 17 +-- src/static/images/bg-settings.svg | 75 ++++++++++ src/styles/components/buttons.css | 22 ++- src/styles/components/cards/challenge.css | 2 +- src/styles/components/cards/widget.css | 4 +- src/styles/components/forms.css | 33 ++++- src/styles/components/lists.css | 2 +- src/styles/components/sections.css | 10 +- src/styles/components/timeline.css | 22 ++- src/styles/pages/leaderboard-row.css | 2 +- src/styles/utilities/backgrounds.css | 4 + src/styles/utilities/positions.css | 9 ++ src/styles/utilities/statuses.css | 14 +- src/tailwind.js | 21 ++- src/theme.scss | 6 +- src/variables.scss | 2 +- 67 files changed, 854 insertions(+), 533 deletions(-) delete mode 100644 src/components/AdminPane/Manage/RebuildTasksControl/RebuildTasksControl.scss delete mode 100644 src/components/ErrorModal/ErrorModal.scss create mode 100644 src/components/ErrorModal/Messages.js create mode 100644 src/pages/Profile/UserSettings/NotificationSettingsSchema.js create mode 100644 src/static/images/bg-settings.svg diff --git a/src/components/ActivityTimeline/ActivityTimeline.scss b/src/components/ActivityTimeline/ActivityTimeline.scss index d6400a798..06648f3f3 100644 --- a/src/components/ActivityTimeline/ActivityTimeline.scss +++ b/src/components/ActivityTimeline/ActivityTimeline.scss @@ -1,22 +1,29 @@ @import '../../variables.scss'; +$markerColor: #F7BB59; +$markerTextColor: $white; +$headingColor: $markerColor; +$badgeColor: #F2EFE9; +$badgeTextColor: #737373; + .timeline.activity-timeline { .timeline-header { + display: none; span { - color: $white; - background-color: $green; + color: $markerTextColor; + background-color: $markerColor; } } .timeline-item { .timeline-marker { - background-color: $green; - border-color: $green; + background-color: $markerColor; + border-color: $markerColor; } .timeline-content { .heading { - color: $coral; + color: $headingColor; margin-top: 2px; } @@ -26,14 +33,14 @@ &::after { top: 0; left: 0; - color: $grey-dark; - background-color: $grey-lightest; + color: $badgeTextColor; + background-color: $badgeColor; box-shadow: none; } &.inverted { &::after { - background-color: $white; + background-color: $markerTextColor; } } } diff --git a/src/components/ActivityTimeline/UserActivityTimeline/UserActivityTimelineWidget.js b/src/components/ActivityTimeline/UserActivityTimeline/UserActivityTimelineWidget.js index 319fe79d9..b4955c27b 100644 --- a/src/components/ActivityTimeline/UserActivityTimeline/UserActivityTimelineWidget.js +++ b/src/components/ActivityTimeline/UserActivityTimeline/UserActivityTimelineWidget.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import classNames from 'classnames' import { FormattedMessage, injectIntl } from 'react-intl' import { Link } from 'react-router-dom' import _map from 'lodash/map' @@ -165,7 +166,7 @@ export default class UserActivityTimelineWidget extends Component { className="user-timeline-activity-widget" widgetTitle={} > -
    +
      {timelineItems}
    diff --git a/src/components/AdminPane/AdminPane.scss b/src/components/AdminPane/AdminPane.scss index cfd66a536..6bc35c70e 100644 --- a/src/components/AdminPane/AdminPane.scss +++ b/src/components/AdminPane/AdminPane.scss @@ -147,9 +147,12 @@ line-height: 16px; } - li.is-active a { - color: $white; - font-weight: $weight-semibold; + li.nav-title a { + font-size: 28px; + } + + li.is-active:not(.nav-title) a { + color: $yellow; .busy-spinner { margin-left: 10px; diff --git a/src/components/AdminPane/Manage/BurndownChart/BurndownChart.js b/src/components/AdminPane/Manage/BurndownChart/BurndownChart.js index 673229c4d..b11c74c17 100644 --- a/src/components/AdminPane/Manage/BurndownChart/BurndownChart.js +++ b/src/components/AdminPane/Manage/BurndownChart/BurndownChart.js @@ -69,7 +69,7 @@ export class BurndownChart extends Component {

    }
    this.props.toggleChallengePin(this.props.challenge.id)}> - +
diff --git a/src/components/AdminPane/Manage/ChallengeDashboard/ChallengeDashboard.js b/src/components/AdminPane/Manage/ChallengeDashboard/ChallengeDashboard.js index 51e96e172..d2027811b 100644 --- a/src/components/AdminPane/Manage/ChallengeDashboard/ChallengeDashboard.js +++ b/src/components/AdminPane/Manage/ChallengeDashboard/ChallengeDashboard.js @@ -113,7 +113,7 @@ export class ChallengeDashboard extends Component {
) } @@ -252,7 +324,9 @@ WithChallengePreferences( WithWidgetWorkspaces( WithTaskBundle( WithSuggestedFix( - injectIntl(TaskPane) + WithLockedTask( + injectIntl(TaskPane) + ) ), ), WidgetDataTarget.task, diff --git a/src/components/TaskTags/TaskTags.js b/src/components/TaskTags/TaskTags.js index 630b303d0..29832c789 100644 --- a/src/components/TaskTags/TaskTags.js +++ b/src/components/TaskTags/TaskTags.js @@ -63,10 +63,12 @@ export class TaskTags extends Component { else if (this.props.tags && this.props.tags !== "") { return (
-
this.setState({edit: true})}> - -
+ {!this.props.taskReadOnly && +
this.setState({edit: true})}> + +
+ }
) } - else { + else if (!this.props.taskReadOnly) { return (
) } + else { + return ( +
+ +
+ ) + } } } diff --git a/src/components/Widgets/TaskBundleWidget/Messages.js b/src/components/Widgets/TaskBundleWidget/Messages.js index dc688116d..b4a72f1a1 100644 --- a/src/components/Widgets/TaskBundleWidget/Messages.js +++ b/src/components/Widgets/TaskBundleWidget/Messages.js @@ -79,4 +79,9 @@ export default defineMessages({ id: "Widgets.TaskBundleWidget.noVirtualChallenges", defaultMessage: "Tasks in \"virtual\" challenges cannot be bundled together", }, + + readOnly: { + id: "Widgets.TaskBundleWidget.readOnly", + defaultMessage: "Previewing task in read-only mode", + }, }) diff --git a/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js b/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js index b05ea29a1..271d92606 100644 --- a/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js +++ b/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js @@ -197,7 +197,7 @@ const ActiveBundle = props => { values={{taskCount: props.taskBundle.taskIds.length}} /> - {!props.disallowBundleChanges && + {!props.taskReadOnly && !props.disallowBundleChanges &&
+ } diff --git a/src/services/Challenge/Challenge.js b/src/services/Challenge/Challenge.js index d956a216e..3b566df6d 100644 --- a/src/services/Challenge/Challenge.js +++ b/src/services/Challenge/Challenge.js @@ -17,6 +17,7 @@ import _isArray from 'lodash/isArray' import _fromPairs from 'lodash/fromPairs' import _isUndefined from 'lodash/isUndefined' import _groupBy from 'lodash/groupBy' +import _join from 'lodash/join' import parse from 'date-fns/parse' import format from 'date-fns/format' import { defaultRoutes as api, isSecurityError } from '../Server/Server' @@ -56,6 +57,36 @@ export const challengeDenormalizationSchema = function() { } // utility functions +/** + * Builds a link to export CSV + */ +export const buildLinkToExportCSV = function(challengeId, criteria) { + const queryFilters = buildQueryFilters(criteria) + return `${process.env.REACT_APP_MAP_ROULETTE_SERVER_URL}/api/v2/challenge/${challengeId}/tasks/extract?${queryFilters}` +} + +/** + * Builds a link to export GeoJSON + */ +export const buildLinkToExportGeoJSON = function(challengeId, criteria) { + const queryFilters = buildQueryFilters(criteria) + return `${process.env.REACT_APP_MAP_ROULETTE_SERVER_URL}/api/v2/challenge/view/${challengeId}?${queryFilters}` +} + +// Helper function to build query filters for export links +const buildQueryFilters = function(criteria) { + const filters = _get(criteria, 'filters', {}) + const taskId = filters.id + const reviewRequestedBy = filters.reviewRequestedBy + const reviewedBy = filters.reviewedBy + return ( + `status=${_join(filters.status, ',')}&` + + `priority=${_join(filters.priorities, ',')}&` + + `reviewStatus=${_join(filters.reviewStatus, ',')}` + + `${taskId ? `&tid=${taskId}` : ""}` + + `${reviewRequestedBy ? `&o=${reviewRequestedBy}` : ""}` + + `${reviewedBy ? `&r=${reviewedBy}` : ""}`) +} /** * Retrieves the resulting challenge entity object from the given From c550e0c01ff224e0fd222813aaa2044ede4c3caa Mon Sep 17 00:00:00 2001 From: Kelli Rotstan Date: Wed, 5 Feb 2020 13:49:01 -0800 Subject: [PATCH 37/38] In auto suggest tag box dont show tags already chosen When showing a dropdown to pick preferred and suggested tags, if the tag has already been selected then don't include it in the list. --- .../AutosuggestTextBox/AutosuggestTextBox.js | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/AutosuggestTextBox/AutosuggestTextBox.js b/src/components/AutosuggestTextBox/AutosuggestTextBox.js index 63746d175..a218b8c32 100644 --- a/src/components/AutosuggestTextBox/AutosuggestTextBox.js +++ b/src/components/AutosuggestTextBox/AutosuggestTextBox.js @@ -5,6 +5,12 @@ import classNames from 'classnames' import Downshift from 'downshift' import _map from 'lodash/map' import _get from 'lodash/get' +import _isEmpty from 'lodash/isEmpty' +import _split from 'lodash/split' +import _difference from 'lodash/difference' +import _clone from 'lodash/clone' +import _indexOf from 'lodash/indexOf' +import _filter from 'lodash/filter' import BusySpinner from '../BusySpinner/BusySpinner' import messages from './Messages' import './AutosuggestTextBox.scss' @@ -72,22 +78,38 @@ export default class AutosuggestTextBox extends Component { ) let items = [] - if (this.props.preferredResults) { + const preferredResults = this.getPreferredResults() + if (!_isEmpty(preferredResults)) { let className = "mr-font-medium" - items = items.concat(_map(this.props.preferredResults, + items = items.concat(_map(preferredResults, (result, index) => { - // Add a border bottom to the last entry - if (index === this.props.preferredResults.length - 1) { + // Add a border bottom to the last entry if there are more + // search results. + if (index === preferredResults.length - 1) { className += " mr-border-b-2 mr-border-grey-lighter mr-mb-2 mr-pb-2" } return generateResult(result, className) })) } - items = items.concat(_map(this.props.searchResults, generateResult)) + items = items.concat(_map(this.getSearchResults(), generateResult)) return items } + getPreferredResults = () => { + // Filter out any tags that have already been selected. + const preferredResults = _clone(this.props.preferredResults) || [] + return _difference(preferredResults, _split(this.props.formData, ',')) + } + + getSearchResults = () => { + // Filter out any of our original preferredResults tags so they don't show + // in the list twice. + return _filter(this.props.searchResults, + t => _indexOf(this.props.preferredResults, t.name) === -1) + + } + render() { return ( Date: Fri, 7 Feb 2020 10:35:25 -0800 Subject: [PATCH 38/38] Bump to v3.5.4 --- CHANGELOG.md | 31 ++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0db293f7..554f63a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,36 @@ The format is based on This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [v3.5.3] - 2020-01-21 +## [v3.5.4] - 2020-02-07 +### Added +- Ability to designate task property as OSM id in challenge creation +- Ability to setup preferred task tags during challenge creation +- Reviewer-related stats on User Metrics page +- Proper links for challenge names on Find Challenges page (#997) +- Past Progress historical progress widget for challenges in Create & Manage +- Ability to filter tasks by id, mapper, and reviewer in Create & Manage +- Option to view tasks actively locked by a mapper in read-only preview mode +- Default to excluding task reviews assigned to others on Task Review page +- Support for basic point/marker "simplestyles" task-feature styling +- Dedicated sign-in page if user navigates to page requiring sign-in +- Various app-wide color and styling updates and fixes +- Minor speed-up to loading of challenge dashboard in Create & Manage +- [internal] Upgrade various package dependencies + +### Fixed +- Incorrect display of 'view by priority' link on Project Detail page +- Occasional discrepency in count of tasks completed together (#1086) +- Erroneous treatment of en-US locale as invalid in User Settings +- Failure during display of Markdown containing explicit line-breaks +- Negative timers during task build or rebuild +- Erroneous option to sort tasks by feature columns, which is not supported +- Incorrect treatment of Current Month in user metrics +- Occasional wrong result when searching for certain users (#1061) +- Potential task loop when skipping tasks at a priority boundary +- [internal] Update unit tests + + +## [v3.5.3] - 2020-01-22 ### Added - Support for `out center` in Overpass queries by @Zverik - Option for project managers to export CSV of tasks in project diff --git a/package.json b/package.json index 49cab31c6..3a96a5dd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maproulette3", - "version": "3.5.3", + "version": "3.5.4", "private": true, "dependencies": { "@mapbox/geo-viewport": "^0.4.0",