Skip to content

Commit

Permalink
Merge pull request #1186 from osmlab/prerelease
Browse files Browse the repository at this point in the history
v3.5.8
  • Loading branch information
nrotstan authored Mar 25, 2020
2 parents 34e754e + bb7977c commit 6e72cab
Show file tree
Hide file tree
Showing 91 changed files with 14,023 additions and 3,405 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ REACT_APP_FEATURE_SOCIAL_SHARING='enabled'
REACT_APP_FEATURE_LEADERBOARD='enabled'
REACT_APP_FEATURE_CHALLENGE_ANALYSIS_TABLE='disabled'
REACT_APP_FEATURE_MOBILE_DEVICES='disabled'
REACT_APP_IMAGERY_OPENSTREETCAM='enabled'
REACT_APP_DEFAULT_MAP_LAYERS='enabled'
REACT_APP_OSM_DATA_OVERLAY='enabled'

# Default locale to use if user has not set their locale. Note that this locale
# must be supported by the app or the default en-US will be used.
Expand Down
9 changes: 9 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com

[maproulette3.en-us-json]
file_filter = src/lang/<lang>.json
minimum_perc = 10
source_file = src/lang/en-US.json
source_lang = en
type = KEYVALUEJSON
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ The format is based on
This project adheres to
[Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [v3.5.8] - 2020-03-25
### Added
- OpenStreetCam imagery map overlay during task completion
- Integration with Transifex for supporting community translations (#734)
- Additional locales: nl, ru-RU, fa-IR, cs-CZ
- Option to preview task comment Markdown during composition
- Link user mentions in comments to mentioned user's metrics page
- Separate rendering colors for nodes, ways, areas in OSM Data map overlay
- Control over display of nodes, ways, areas in OSM Data map overlay
- Enter and ESC keyboard shortcuts in Nominatim search box
- Show busy spinner while performing Nominatim search
- Show busy spinner while geolocating user when tasks Near Me requested
- Show administrative updates to tasks by challenge managers in task history
- Filtering by task priority in Task Review
- Ability to adjust task-review filters after each reviewed task
- Notify task reviewer when re-review requested after follow-up changes
- Make some widgets permanent in various Create & Manage dashboards
- Option to export project CSVs from main Create & Manage dashboard
- URLs to tasks and challenges in Project CSVs
- Display of mapper in Create & Manage tables for all tasks going forward
- New color palette for colored usernames shown in various tables
- Various minor UI and color adjustments
- Challenge flag indicating that local knowledge is required to work on tasks
- Server admin configuration option to disable default map layers
- Server admin configuration option to hide OSM Data map overlay

### Fixed
- Layer toggle unresponsive after failure to load map layer (#1177)
- Hiding task-feature map layer can cause map zoom to reset (#1167)
- Removing keywords from cloned challenge causes save to fail (#1188)
- Empty options menus for some challenges in Create & Manage
- Task completion keyboard shortcuts active during confirmation step
- Erroneous calculation of time spent on task if server clocks different
- Occasional display of old task review status due to stale cache


## [v3.5.7] - 2020-03-11
### Added
- Dark mode in Create & Manage area
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,22 @@ are in play.
Tailwind configuration is controlled with the `src/tailwind.config.js` file.
New CSS classes can be found in `src/styles/`

## Internationalization and Localization
## Internationalization, Localization, and Translation

Internationalization and localization is performed via
[react-intl](https://github.com/yahoo/react-intl/wiki). Most components feature
co-located Messages.js files that contain messages intended for display,
along with default (U.S. English) versions of each message. Translation files
that contain translated versions of these messages for supported locales are
stored in the `src/lang/` directory. A fresh en-US.json file can be built from
the latest messages using `yarn run build-intl`, which is also run
automatically as part of the `yarn build` script used for creating production
builds. Translation files for other locales must be updated manually.
along with default (U.S. English) versions of each message.

A fresh en-US.json file can be built from the latest messages using `yarn run
build-intl`, which is also run automatically as part of the `yarn build` script
used for creating production builds.

Translations for other locales are managed through
[transifex](https://www.transifex.com/osmlab/maproulette3), who kindly provides
us with free service through their Open Source program. Translation files are
pulled into the code repository from time to time and stored in the `src/lang/`
directory.

By default, the en-US locale will be used for users who have not set a locale in
their MapRoulette user settings. This default locale can be changed with the
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "maproulette3",
"version": "3.5.7",
"version": "3.5.8",
"private": true,
"dependencies": {
"@mapbox/geo-viewport": "^0.4.0",
Expand Down Expand Up @@ -109,9 +109,10 @@
"watch-postcss": "postcss src/styles/index.css -o src/index.css -w",
"build-intl": "yarn run combine-messages -i './src/**/*.js' -o './src/lang/en-US.json'",
"update-layers": "node scripts/update_layers.js",
"update-layers-prod": "NODE_ENV=production node scripts/update_layers.js",
"start-js": "react-scripts start",
"start": "npm-run-all -p update-layers watch-postcss start-js",
"build": "yarn run build-intl && yarn run update-layers && yarn run build-postcss && react-scripts build",
"build": "yarn run build-intl && yarn run update-layers-prod && yarn run build-postcss && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Expand Down
37 changes: 28 additions & 9 deletions scripts/update_layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
* Requires curl and jq to both be installed.
*/
require('dotenv').config()
const dotenv = require('dotenv')
const _get = require('lodash/get')
const shell = require('shelljs')
shell.config.silent = true
Expand All @@ -31,6 +31,19 @@ if (shell.ls('./src').length === 0) {
shell.exit(1)
}

// Load .env configuration files. Try loading .env.production or
// .env.development.local first (depending on NODE_ENV), and then fill in any
// gaps from the default .env file
try {
dotenv.config({
path: process.env.NODE_ENV === 'production' ?
'.env.production' :
'.env.development.local'
})
}
catch(error) {}
dotenv.config() // fill in from normal .env file

// Additional layers (from the Layer Index) to include in addition to the
// default layers.
const additionalIndexLayers =
Expand All @@ -43,14 +56,20 @@ if (shell.exec("curl -s https://osmlab.github.io/editor-layer-index/imagery.geoj
shell.exit(1)
}

// Extract properties from layers marked as default layers that have global
// coverage (geometry is null) and save them to `src/defaultLayers.json`. We
// also include any "additional" layers requested
shell.echo("Extracting default layers")
const jqLayerConditionals = additionalIndexLayers.map(layerId => ` or .properties.id == "${layerId}"`).join(' ')
if (shell.exec("jq '[.features[] | select((.properties.default == true and .properties.geometry == null)" + jqLayerConditionals + ")]' ./src/imagery.json > ./src/defaultLayers.json").code !== 0) {
shell.echo("Extracting default layers failed")
shell.exit(1)
// Unless default layers are disabled, extract properties from layers marked as
// default layers that have global coverage (geometry is null) and save them to
// `src/defaultLayers.json`. We also include any "additional" layers requested
if (_get(process.env, 'REACT_APP_DEFAULT_MAP_LAYERS', 'enabled') === 'disabled') {
shell.echo("Default map layers have been disabled. Ignoring default layers.")
shell.echo('[]').to('./src/defaultLayers.json')
}
else {
shell.echo("Extracting default layers")
const jqLayerConditionals = additionalIndexLayers.map(layerId => ` or .properties.id == "${layerId}"`).join(' ')
if (shell.exec("jq '[.features[] | select((.properties.default == true and .properties.geometry == null)" + jqLayerConditionals + ")]' ./src/imagery.json > ./src/defaultLayers.json").code !== 0) {
shell.echo("Extracting default layers failed")
shell.exit(1)
}
}

// Users can add custom layers to `src/customLayers.json`. If the file does not
Expand Down
60 changes: 36 additions & 24 deletions src/components/AdminPane/Manage/ChallengeCard/ChallengeCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'
import { FormattedMessage, injectIntl } from 'react-intl'
import _get from 'lodash/get'
import _isFinite from 'lodash/isFinite'
import _isObject from 'lodash/isObject'
import AsManageableChallenge
from '../../../../interactions/Challenge/AsManageableChallenge'
import WithChallengeManagement
Expand All @@ -31,6 +32,15 @@ export class ChallengeCard extends Component {
return null
}

let parent = null
if (_isObject(this.props.challenge.parent)) {
parent = this.props.challenge.parent
}
else if (_isFinite(this.props.challenge.parent) &&
this.props.challenge.parent === _get(this.props, 'project.id')) {
parent = this.props.project
}

const hasActions = _isFinite(_get(this.props.challenge, 'actions.total'))

const ChallengeIcon = AsManageableChallenge(this.props.challenge).isComplete() ?
Expand Down Expand Up @@ -61,7 +71,7 @@ export class ChallengeCard extends Component {
</Link>
{this.props.showProjectName &&
<div className="mr-text-xs mr-text-grey-light">
{_get(this.props.challenge, 'parent.displayName')}
{_get(parent, 'displayName')}
</div>
}
{hasActions &&
Expand Down Expand Up @@ -115,29 +125,31 @@ export class ChallengeCard extends Component {
</div>
}

<Dropdown
className="mr-ml-4 mr-dropdown--right mr-mt-1.5"
dropdownButton={dropdown => (
<button
onClick={dropdown.toggleDropdownVisible}
className="mr-flex mr-items-center mr-text-white"
>
<SvgSymbol
sym="navigation-more-icon"
viewBox="0 0 20 20"
className="mr-fill-current mr-w-5 mr-h-5"
/>
</button>
)}
dropdownContent={dropdown =>
<ChallengeControls
{...this.props}
className="mr-flex mr-flex-col mr-links-green-lighter"
controlClassName="mr-my-1"
onControlComplete={() => dropdown.closeDropdown()}
/>
}
/>
{parent &&
<Dropdown
className="mr-ml-4 mr-dropdown--right mr-mt-1.5"
dropdownButton={dropdown => (
<button
onClick={dropdown.toggleDropdownVisible}
className="mr-flex mr-items-center mr-text-white"
>
<SvgSymbol
sym="navigation-more-icon"
viewBox="0 0 20 20"
className="mr-fill-current mr-w-5 mr-h-5"
/>
</button>
)}
dropdownContent={dropdown =>
<ChallengeControls
{...this.props}
className="mr-flex mr-flex-col mr-links-green-lighter"
controlClassName="mr-my-1"
onControlComplete={() => dropdown.closeDropdown()}
/>
}
/>
}
</div>
</div>
)
Expand Down
35 changes: 24 additions & 11 deletions src/components/AdminPane/Manage/ChallengeCard/ChallengeControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import { Link } from 'react-router-dom'
import _get from 'lodash/get'
import _isObject from 'lodash/isObject'
import _isFinite from 'lodash/isFinite'
import AsManager from '../../../../interactions/User/AsManager'
import AsManageableChallenge
from '../../../../interactions/Challenge/AsManageableChallenge'
Expand All @@ -29,17 +31,29 @@ export default class ChallengeControls extends Component {
this.props.onControlComplete && this.props.onControlComplete()
}

deleteChallenge = () => {
this.props.deleteChallenge(this.props.challenge.parent.id,
this.props.challenge.id)
deleteChallenge = parent => {
this.props.deleteChallenge(parent.id, this.props.challenge.id)
}

render() {
if (!this.props.challenge) {
return null
}

let parent = null
if (_isObject(this.props.challenge.parent)) {
parent = this.props.challenge.parent
}
else if (_isFinite(this.props.challenge.parent) &&
this.props.challenge.parent === _get(this.props, 'project.id')) {
parent = this.props.project
}

const inVirtualProject = _get(this.props, 'project.isVirtual', false)
const manager = AsManager(this.props.user)
const projectId = _get(this.props, 'challenge.parent.id')
const status = _get(this.props, 'challenge.status', ChallengeStatus.none)
const hasTasks = _get(this.props, 'challenge.actions.total', 0) > 0
const projectId = _get(this.props.challenge, 'parent.id', this.props.challenge.parent)
const status = _get(this.props.challenge, 'status', ChallengeStatus.none)
const hasTasks = _get(this.props.challenge, 'actions.total', 0) > 0

return (
<div className={this.props.className}>
Expand All @@ -52,8 +66,7 @@ export default class ChallengeControls extends Component {
</Link>
}

{!inVirtualProject &&
manager.canWriteProject(this.props.challenge.parent) &&
{!inVirtualProject && manager.canWriteProject(parent) &&
<React.Fragment>
<Link
to={`/admin/project/${projectId}/` +
Expand All @@ -63,7 +76,7 @@ export default class ChallengeControls extends Component {
<FormattedMessage {...messages.editChallengeLabel } />
</Link>

{manager.canAdministrateProject(this.props.challenge.parent) &&
{manager.canAdministrateProject(parent) &&
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a
onClick={() => this.setState({pickingProject: true})}
Expand All @@ -89,11 +102,11 @@ export default class ChallengeControls extends Component {
<FormattedMessage {...messages.cloneChallengeLabel } />
</Link>

{manager.canAdministrateProject(this.props.challenge.parent) &&
{manager.canAdministrateProject(parent) &&
<ConfirmAction>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a
onClick={this.deleteChallenge}
onClick={() => this.deleteChallenge(parent)}
className={this.props.controlClassName}
>
<FormattedMessage {...messages.deleteChallengeLabel } />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class ChallengeList extends Component {
)

return (
<div className="mr-text-base mr-pb-1">
<div className="mr-text-base mr-pb-1 mr-pb-36">

{!this.props.loadingChallenges && challengeCards.length === 0 ?
<div className="mr-flex mr-justify-center mr-text-grey-light">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,5 +519,16 @@ will not be able to make sense of it.
taskPropertyStylesDescription: {
id: "Admin.EditChallenge.form.taskPropertyStyles.description",
defaultMessage: "Sets up task property style rules......"
},

requiresLocalLabel: {
id: "Admin.EditChallenge.form.requiresLocal.label",
defaultMessage: "Requires Local Knowledge"
},

requiesLocalDescription: {
id: "Admin.EditChallenge.form.requiresLocal.description",
defaultMessage: "Tasks require local or on-the-ground knowledge to complete." +
" Note: challenge will not appear in the 'Find challenges' list."
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ export const jsSchema = intl => {
type: "boolean",
default: false,
},
requiresLocal: {
title: intl.formatMessage(messages.requiresLocalLabel),
type: "boolean",
default: false,
},
},
dependencies: { // Only show customBasemap if defaultBasemap set to Custom
defaultBasemap: {
Expand Down Expand Up @@ -170,6 +175,10 @@ export const uiSchema = intl => ({
customTaskStyles: {
"ui:field": "configureCustomTaskStyles",
"ui:help": intl.formatMessage(messages.customTaskStylesDescription),
},
requiresLocal: {
"ui:widget": "radio",
"ui:help": intl.formatMessage(messages.requiesLocalDescription),
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export const defaultDashboardSetup = function() {
widgetDescriptor('ProjectManagersWidget'),
widgetDescriptor('ChallengeListWidget'),
],
permanentWidgets: [ // Cannot be removed from workspace
'ChallengeListWidget',
],
layout: [
{i: generateWidgetId(), x: 0, y: 0, w: 4, h: 7},
{i: generateWidgetId(), x: 0, y: 7, w: 4, h: 7},
Expand Down
Loading

0 comments on commit 6e72cab

Please sign in to comment.