├── src
├── App.scss
├── components
│ ├── AdminPane
│ │ ├── Manage
│ │ │ ├── GridBlocks
│ │ │ │ ├── .gitignore
│ │ │ │ ├── ChallengeListBlock
│ │ │ │ │ ├── ChallengeListBlock.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── CommentsBlock
│ │ │ │ │ ├── CommentsBlock.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── LeaderboardBlock
│ │ │ │ │ ├── LeaderboardBlock.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── ProjectAboutBlock
│ │ │ │ │ ├── ProjectAboutBlock.scss
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── ProjectAboutBlock.js
│ │ │ │ ├── BurndownChartBlock
│ │ │ │ │ ├── BurndownChartBlock.scss
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── BurndownChartBlock.js
│ │ │ │ ├── MenuControlDivider.js
│ │ │ │ ├── StatusRadarBlock
│ │ │ │ │ ├── StatusRadarBlock.scss
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── StatusRadarBlock.js
│ │ │ │ ├── CalendarHeatmapBlock
│ │ │ │ │ ├── CalendarHeatmapBlock.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── ChallengeTasksBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── ChallengeTasksBlock.js
│ │ │ │ ├── MenuControl.js
│ │ │ │ ├── ProjectCountBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── ProjectCountBlock.js
│ │ │ │ ├── ProjectOverviewBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── ProjectOverviewBlock.scss
│ │ │ │ ├── RecentActivityBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── RecentActivityBlock.js
│ │ │ │ ├── ProjectListBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── ProjectListBlock.scss
│ │ │ │ ├── CompletionProgressBlock
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── CompletionProgressBlock.js
│ │ │ │ └── ChallengeOverviewBlock
│ │ │ │ │ ├── ChallengeOverviewBlock.scss
│ │ │ │ │ └── Messages.js
│ │ │ ├── StepNavigation
│ │ │ │ ├── StepNavigation.scss
│ │ │ │ └── Messages.js
│ │ │ ├── GridBlockPicker
│ │ │ │ ├── GridBlockPicker.scss
│ │ │ │ └── Messages.js
│ │ │ ├── ReviewTask
│ │ │ │ ├── ReviewTask.scss
│ │ │ │ └── Messages.js
│ │ │ ├── EditProject
│ │ │ │ └── EditProject.scss
│ │ │ ├── ChallengeKeywords
│ │ │ │ ├── ChallengeKeywords.scss
│ │ │ │ ├── ChallengeKeywords.test.js
│ │ │ │ └── ChallengeKeywords.js
│ │ │ ├── Messages.js
│ │ │ ├── ProjectOverview
│ │ │ │ ├── ProjectOverview.scss
│ │ │ │ └── Messages.js
│ │ │ ├── CalendarHeatmap
│ │ │ │ ├── Messages.js
│ │ │ │ └── CalendarHeatmap.scss
│ │ │ ├── CompletionRadar
│ │ │ │ └── Messages.js
│ │ │ ├── KeywordAutosuggestInput
│ │ │ │ ├── Messages.js
│ │ │ │ └── KeywordAutosuggestInput.scss
│ │ │ ├── ManageTasks
│ │ │ │ └── Messages.js
│ │ │ ├── BurndownChart
│ │ │ │ └── Messages.js
│ │ │ ├── ChallengeList
│ │ │ │ ├── Messages.js
│ │ │ │ └── ChallengeList.scss
│ │ │ ├── ProjectDashboard
│ │ │ │ └── Messages.js
│ │ │ ├── TaskUploadingProgress
│ │ │ │ └── Messages.js
│ │ │ ├── ManageChallenges
│ │ │ │ └── EditChallenge
│ │ │ │ │ └── EditChallenge.scss
│ │ │ ├── ProjectLeaderboard
│ │ │ │ └── ProjectLeaderboard.js
│ │ │ ├── ChallengeOwnerLeaderboard
│ │ │ │ └── ChallengeOwnerLeaderboard.js
│ │ │ ├── Dashboard
│ │ │ │ ├── Messages.js
│ │ │ │ └── Dashboard.scss
│ │ │ ├── ProjectsDashboard
│ │ │ │ ├── ProjectsDashboard.scss
│ │ │ │ └── Messages.js
│ │ │ ├── DashboardFilterToggle
│ │ │ │ └── DashboardFilterToggle.js
│ │ │ ├── ChallengeAnalysisTable
│ │ │ │ └── ChallengeAnalysisTable.scss
│ │ │ ├── ChallengeTaskMap
│ │ │ │ └── Messages.js
│ │ │ ├── TaskAnalysisTable
│ │ │ │ └── TaskAnalysisTable.scss
│ │ │ ├── ChallengeDashboard
│ │ │ │ └── Messages.js
│ │ │ ├── ViewTask
│ │ │ │ └── ViewTask.js
│ │ │ ├── ProjectManagers
│ │ │ │ ├── ProjectManagers.scss
│ │ │ │ └── Messages.js
│ │ │ ├── RebuildTasksControl
│ │ │ │ └── RebuildTasksControl.scss
│ │ │ ├── ProjectCard
│ │ │ │ └── Messages.js
│ │ │ ├── ProjectList
│ │ │ │ └── ProjectList.scss
│ │ │ └── ChallengeFilterGroup
│ │ │ │ └── ChallengeFilterGroup.js
│ │ ├── MetricsOverview
│ │ │ ├── Messages.js
│ │ │ └── MetricsOverview.scss
│ │ └── HOCs
│ │ │ ├── WithWideScreenOption
│ │ │ └── WithWideScreenOption.js
│ │ │ └── WithComboSearch
│ │ │ └── WithComboSearch.js
│ ├── Sprites
│ │ └── Sprites.scss
│ ├── TaskPane
│ │ ├── TaskMap
│ │ │ ├── TaskMap.scss
│ │ │ └── TaskMap.test.js
│ │ ├── TaskPane.scss
│ │ ├── ActiveTaskDetails
│ │ │ ├── CollapsibleSection
│ │ │ │ └── CollapsibleSection.scss
│ │ │ ├── ActiveTaskControls
│ │ │ │ ├── TaskCancelEditingControl
│ │ │ │ │ ├── TaskCancelEditingControl.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── MoreOptionsControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskCommentInput
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ ├── TaskCommentInput.scss
│ │ │ │ │ ├── TaskCommentInput.test.js
│ │ │ │ │ └── TaskCommentInput.js
│ │ │ │ ├── TaskEditControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskNextControl
│ │ │ │ │ ├── Messages.js
│ │ │ │ │ └── TaskNextControl.scss
│ │ │ │ ├── TaskSkipControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskFixedControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskCompletionStep2
│ │ │ │ │ ├── TaskCompletionStep2.scss
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskAlreadyFixedControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskTooHardControl
│ │ │ │ │ └── Messages.js
│ │ │ │ ├── TaskFalsePositiveControl
│ │ │ │ │ └── Messages.js
│ │ │ │ └── TaskCompletionStep1
│ │ │ │ │ └── TaskCompletionStep1.scss
│ │ │ ├── ReviewTaskControls
│ │ │ │ ├── ReviewTaskControls.scss
│ │ │ │ └── Messages.js
│ │ │ ├── KeyboardShortcutReference
│ │ │ │ ├── Messages.js
│ │ │ │ └── KeyboardShortcutReference.scss
│ │ │ ├── TaskStatusIndicator
│ │ │ │ └── Messages.js
│ │ │ └── Messages.js
│ │ ├── MobileTaskDetails
│ │ │ └── Messages.js
│ │ ├── TaskRandomnessControl
│ │ │ ├── Messages.js
│ │ │ └── TaskRandomnessControl.scss
│ │ ├── VirtualChallengeNameLink
│ │ │ └── Messages.js
│ │ ├── TaskLatLon
│ │ │ └── TaskLatLon.scss
│ │ ├── TaskTrackControls
│ │ │ └── Messages.js
│ │ ├── TaskManageControls
│ │ │ ├── TaskManageControls.scss
│ │ │ └── Messages.js
│ │ ├── OwnerContactLink
│ │ │ └── Messages.js
│ │ ├── ChallengeShareControls
│ │ │ ├── ChallengeShareControls.scss
│ │ │ └── ChallengeShareControls.test.js
│ │ ├── TaskLocationMap
│ │ │ ├── TaskLocationMap.scss
│ │ │ └── TaskLocationMap.js
│ │ └── ChallengeNameLink
│ │ │ └── ChallengeNameLink.js
│ ├── LoadMoreButton
│ │ ├── LoadMoreButton.scss
│ │ └── Messages.js
│ ├── ChallengePane
│ │ ├── ChallengeResultList
│ │ │ ├── LoadMoreButton.scss
│ │ │ ├── SortChallengesSelector.scss
│ │ │ └── Messages.js
│ │ ├── ChallengeFilterSubnav
│ │ │ ├── OtherKeywordsOption.js
│ │ │ ├── Messages.js
│ │ │ └── ChallengeFilterSubnav.test.js
│ │ └── ChallengePane.test.js
│ ├── Bulma
│ │ ├── SimpleDropdown.scss
│ │ ├── LabeledProgressBar.scss
│ │ ├── Steps.scss
│ │ ├── Menu.scss
│ │ ├── RJSFFormFieldAdapter
│ │ │ └── Messages.js
│ │ ├── TriStateCheckbox.js
│ │ ├── Menu.js
│ │ ├── SvgControl.js
│ │ └── Modal.js
│ ├── HOCs
│ │ ├── WithEditor
│ │ │ ├── __snapshots__
│ │ │ │ └── WithEditor.test.js.snap
│ │ │ └── WithEditor.js
│ │ ├── WithErrors
│ │ │ ├── __snapshots__
│ │ │ │ └── WithErrors.test.js.snap
│ │ │ └── WithErrors.js
│ │ ├── WithProjects
│ │ │ ├── __snapshots__
│ │ │ │ └── WithProjects.test.js.snap
│ │ │ ├── WithProjects.js
│ │ │ └── WithProjects.test.js
│ │ ├── WithLayerSources
│ │ │ └── Messages.js
│ │ ├── WithCurrentUser
│ │ │ └── __snapshots__
│ │ │ │ └── WithCurrentUser.test.js.snap
│ │ ├── WithStatus
│ │ │ ├── WithStatus.js
│ │ │ └── __snapshots__
│ │ │ │ └── WithStatus.test.js.snap
│ │ ├── WithCurrentTask
│ │ │ └── __snapshots__
│ │ │ │ └── WithCurrentTask.test.js.snap
│ │ ├── WithTaskCenterPoint
│ │ │ └── WithTaskCenterPoint.js
│ │ ├── WithClusteredTasks
│ │ │ └── WithClusteredTasks.js
│ │ ├── WithProgress
│ │ │ └── WithProgress.js
│ │ └── WithCommandInterpreter
│ │ │ └── WithCommandInterpreter.test.js
│ ├── InsetMap
│ │ ├── InsetMap.scss
│ │ └── InsetMap.test.js
│ ├── BusySpinner
│ │ ├── BusySpinner.scss
│ │ └── BusySpinner.js
│ ├── SignInButton
│ │ ├── SignInButton.scss
│ │ └── Messages.js
│ ├── EnhancedMap
│ │ ├── FitBoundsControl
│ │ │ ├── FitBoundsControl.scss
│ │ │ └── Messages.js
│ │ ├── LayerToggle
│ │ │ ├── Messages.js
│ │ │ └── LayerToggle.scss
│ │ └── MapPane
│ │ │ └── MapPane.js
│ ├── HelpPopout
│ │ ├── Messages.js
│ │ └── HelpPopout.scss
│ ├── ShareLink
│ │ ├── Messages.js
│ │ └── ShareLink.scss
│ ├── MapillaryViewer
│ │ └── MapillaryViewer.scss
│ ├── CommentList
│ │ ├── Messages.js
│ │ ├── CommentCountBadge
│ │ │ ├── CommentCountBadge.test.js
│ │ │ ├── CommentCountBadge.scss
│ │ │ └── CommentCountBadge.js
│ │ └── CommentList.scss
│ ├── AutosuggestTextBox
│ │ ├── Messages.js
│ │ └── AutosuggestTextBox.scss
│ ├── UserProfile
│ │ ├── TopChallenges
│ │ │ └── Messages.js
│ │ ├── SavedTasks
│ │ │ └── Messages.js
│ │ ├── SavedChallenges
│ │ │ └── Messages.js
│ │ ├── Messages.js
│ │ ├── PersonalInfo.js
│ │ └── UserProfile.test.js
│ ├── ChallengeSearchMap
│ │ └── Messages.js
│ ├── Ribbon
│ │ ├── Ribbon.js
│ │ └── Ribbon.scss
│ ├── PastDurationSelector
│ │ ├── Messages.js
│ │ └── PastDurationSelector.scss
│ ├── ChallengeProgress
│ │ ├── ChallengeProgress.scss
│ │ ├── Messages.js
│ │ └── ChallengeProgress.test.js
│ ├── Navbar
│ │ ├── AccountNavItem
│ │ │ ├── Messages.js
│ │ │ ├── AccountNavItem.scss
│ │ │ └── AccountNavItem.test.js
│ │ ├── Messages.js
│ │ └── Navbar.test.js
│ ├── PageNotFound
│ │ ├── Messages.js
│ │ └── PageNotFound.scss
│ ├── ScreenTooNarrow
│ │ ├── Messages.js
│ │ ├── ScreenTooNarrow.scss
│ │ └── ScreenTooNarrow.js
│ ├── CongratulateModal
│ │ └── Messages.js
│ ├── ConfirmAction
│ │ ├── ConfirmAction.scss
│ │ └── Messages.js
│ ├── QuickTextBox
│ │ └── QuickTextBox.scss
│ ├── SvgSymbol
│ │ ├── SvgSymbol.test.js
│ │ └── SvgSymbol.js
│ ├── ProjectLeaderboard
│ │ └── ProjectLeaderboard.js
│ ├── MobileFilterMenu
│ │ └── MobileFilterMenu.scss
│ ├── ChallengeLeaderboard
│ │ └── ChallengeLeaderboard.js
│ ├── MobileNotSupported
│ │ ├── Messages.js
│ │ └── MobileNotSupported.scss
│ ├── ErrorModal
│ │ ├── ErrorModal.test.js
│ │ └── ErrorModal.scss
│ ├── ActivityTimeline
│ │ └── ActivityTimeline.scss
│ ├── MarkdownContent
│ │ └── MarkdownContent.js
│ ├── HomePane
│ │ └── Messages.js
│ └── Leaderboard
│ │ └── Messages.js
├── services
│ ├── Server
│ │ ├── RequestStatus.js
│ │ ├── RequestCache.js
│ │ └── Route.test.js
│ ├── Challenge
│ │ ├── ChallengeActions.js
│ │ ├── ChallengeType
│ │ │ ├── Messages.js
│ │ │ └── ChallengeType.js
│ │ ├── ChallengeDifficulty
│ │ │ ├── Messages.js
│ │ │ └── ChallengeDifficulty.js
│ │ ├── ChallengeZoom
│ │ │ └── ChallengeZoom.js
│ │ ├── ChallengeLocation
│ │ │ └── Messages.js
│ │ ├── ChallengeStatus
│ │ │ └── Messages.js
│ │ ├── ChallengeBasemap
│ │ │ └── Messages.js
│ │ └── ChallengeKeywords
│ │ │ └── Messages.js
│ ├── Task
│ │ ├── TaskLoadMethod
│ │ │ ├── Messages.js
│ │ │ └── TaskLoadMethod.js
│ │ ├── TaskPriority
│ │ │ ├── Messages.js
│ │ │ └── TaskPriority.js
│ │ ├── TaskAction
│ │ │ └── TaskAction.js
│ │ └── TaskStatus
│ │ │ └── Messages.js
│ ├── Dashboard
│ │ ├── ChallengeFilter
│ │ │ ├── Messages.js
│ │ │ └── ChallengeFilter.js
│ │ ├── ProjectFilter
│ │ │ ├── Messages.js
│ │ │ └── ProjectFilter.js
│ │ └── Dashboard.js
│ ├── Project
│ │ └── GroupType
│ │ │ └── Messages.js
│ ├── Search
│ │ └── Messages.js
│ ├── User
│ │ └── Locale
│ │ │ └── Messages.js
│ ├── Editor
│ │ └── Messages.js
│ ├── VisibleLayer
│ │ └── VisibleLayer.js
│ ├── Activity
│ │ ├── ActivityItemTypes
│ │ │ ├── Messages.js
│ │ │ └── ActivityItemTypes.js
│ │ └── ActivityActionTypes
│ │ │ └── Messages.js
│ ├── OSMUser
│ │ └── OSMUser.js
│ └── Leaderboard
│ │ └── Leaderboard.js
├── setupTests.js
├── index.scss
└── interactions
│ ├── Overpass
│ ├── Messages.js
│ └── AsValidatableOverpass.js
│ ├── Project
│ └── AsManageableProject.js
│ ├── Challenge
│ └── AsMappableChallenge.js
│ └── User
│ ├── AsMappingUser.js
│ └── AsEndUser.js
├── public
├── favicon.ico
└── manifest.json
├── chimp.example.js
├── .travis.yml
├── features
├── pages
│ ├── Page.js
│ └── OpenStreetMap.js
├── steps
│ └── HomePageSteps.js
└── Signin.feature
├── .gitignore
└── LICENSE.md
/src/App.scss:
--------------------------------------------------------------------------------
1 | .App {
2 | width: 100%;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/.gitignore:
--------------------------------------------------------------------------------
1 | contrib/
2 |
--------------------------------------------------------------------------------
/src/components/Sprites/Sprites.scss:
--------------------------------------------------------------------------------
1 | .sprites {
2 | display: none;
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zverik/maproulette3/master/public/favicon.ico
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskMap/TaskMap.scss:
--------------------------------------------------------------------------------
1 | .task-map {
2 | position: relative;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ChallengeListBlock/ChallengeListBlock.scss:
--------------------------------------------------------------------------------
1 | .challenge-list-block {
2 | }
3 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/CommentsBlock/CommentsBlock.scss:
--------------------------------------------------------------------------------
1 | .comments-block {
2 | .export-control {
3 | margin-right: 10px;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskPane.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .task-pane {
4 | position: relative;
5 | background-color: $grey-lighter;
6 | }
7 |
--------------------------------------------------------------------------------
/chimp.example.js:
--------------------------------------------------------------------------------
1 | // Copy this file to chimp.js and update the settings.
2 |
3 | module.exports = {
4 | mr3URL: "https://maproulette.mydevserver.com/mr3",
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/LoadMoreButton/LoadMoreButton.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins.scss';
2 |
3 | button.load-more-button {
4 | @include invert-on-hover($green, $white);
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/LeaderboardBlock/LeaderboardBlock.scss:
--------------------------------------------------------------------------------
1 | .leaderboard-block {
2 | .past-duration-selector {
3 | margin-right: 10px;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ChallengePane/ChallengeResultList/LoadMoreButton.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins.scss';
2 |
3 | button.load-more-button {
4 | @include invert-on-hover($green, $white);
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/StepNavigation/StepNavigation.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .step-navigation {
4 | display: flex;
5 | justify-content: space-between;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Bulma/SimpleDropdown.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .SimpleDropdown {
4 | .dropdown-trigger {
5 | &:hover {
6 | cursor: pointer;
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/services/Server/RequestStatus.js:
--------------------------------------------------------------------------------
1 | const RequestStatus = Object.freeze({
2 | inProgress: 'in progress',
3 | success: 'success',
4 | error: 'error',
5 | })
6 |
7 | export default RequestStatus
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - "8"
5 | cache:
6 | yarn: true
7 | directories:
8 | - node_modules
9 | script:
10 | - yarn run build
11 | - yarn test
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectAboutBlock/ProjectAboutBlock.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins.scss';
2 |
3 | .project-about-block {
4 | .grid-block__content {
5 | @include markdown-content();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlockPicker/GridBlockPicker.scss:
--------------------------------------------------------------------------------
1 | .grid-block-picker {
2 | margin-right: 15px;
3 |
4 | .dropdown-trigger {
5 | .basic-dropdown-indicator {
6 | top: -1px;
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ReviewTask/ReviewTask.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../variables.scss';
2 |
3 | .admin__manage.review-task {
4 | .task-pane {
5 | border: 1px solid $grey-lightest;
6 | overflow: hidden;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/EditProject/EditProject.scss:
--------------------------------------------------------------------------------
1 | .edit-project {
2 | .title {
3 | display: flex;
4 | justify-content: flex-start;
5 |
6 | .busy-spinner {
7 | margin-left: 1em;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithEditor/__snapshots__/WithEditor.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`mapStateToProps provides an editor prop with the current open editor 1`] = `
4 | Object {
5 | "editor": 1,
6 | }
7 | `;
8 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeActions.js:
--------------------------------------------------------------------------------
1 | // This needs to be separated from Challenge to avoid a circular
2 | // dependency with Project.
3 | export const RECEIVE_CHALLENGES = 'RECEIVE_CHALLENGES'
4 | export const REMOVE_CHALLENGE = 'REMOVE_CHALLENGE'
5 |
--------------------------------------------------------------------------------
/src/components/InsetMap/InsetMap.scss:
--------------------------------------------------------------------------------
1 | @import '../../variables.scss';
2 |
3 | .inset-map {
4 | .leaflet-container {
5 | height: 200px;
6 | border-radius: $radius-medium;
7 |
8 | .leaflet-pane {
9 | z-index: $intralayer-bump;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/BusySpinner/BusySpinner.scss:
--------------------------------------------------------------------------------
1 | @import '../../theme.scss';
2 |
3 | .busy-spinner {
4 | &.inline {
5 | display: inline-block;
6 | margin-left: 5px;
7 | margin-right: 5px;
8 | }
9 |
10 | .busy-spinner-icon {
11 | @include loader;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/BurndownChartBlock/BurndownChartBlock.scss:
--------------------------------------------------------------------------------
1 | .burndown-chart-block {
2 | .burndown-chart {
3 | height: 375px;
4 |
5 | div {
6 | max-height: 100%;
7 | }
8 |
9 | svg {
10 | max-height: 370px;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/SignInButton/SignInButton.scss:
--------------------------------------------------------------------------------
1 | @import '../../variables.scss';
2 | @import '../../mixins.scss';
3 |
4 | .button.signin-button {
5 | @include invert-on-hover($primary, $white);
6 |
7 | &.white-on-green {
8 | @include invert-on-hover($white, $green);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/EnhancedMap/FitBoundsControl/FitBoundsControl.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .fit-bounds-control {
4 | svg {
5 | width: 19px;
6 | height: auto;
7 | fill: $green;
8 | }
9 |
10 | &:hover {
11 | background-color: $grey-lightest-more;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/CollapsibleSection/CollapsibleSection.scss:
--------------------------------------------------------------------------------
1 | .collapsible-section {
2 | &__heading {
3 | position: relative;
4 | display: flex;
5 | justify-content: space-between;
6 |
7 | &:hover {
8 | cursor: pointer;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/HelpPopout/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with HelpPopout.
5 | */
6 | export default defineMessages({
7 | help: {
8 | id: "HelpPopout.control.label",
9 | defaultMessage: "Help"
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/ShareLink/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ShareLink.
5 | */
6 | export default defineMessages({
7 | copy: {
8 | id: 'ShareLink.controls.copy.label',
9 | defaultMessage: "Copy",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeKeywords/ChallengeKeywords.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .challenge-keywords {
4 | display: flex;
5 | flex-wrap: wrap;
6 |
7 | .tag {
8 | margin-right: 5px;
9 | color: $grey;
10 | background-color: $grey-lightest-less;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/SignInButton/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with SigninButton.
5 | */
6 | export default defineMessages({
7 | control: {
8 | id: 'SignIn.control.label',
9 | defaultMessage: "Sign in",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/MapillaryViewer/MapillaryViewer.scss:
--------------------------------------------------------------------------------
1 | @import "variables.scss";
2 |
3 | .mapillary-viewer {
4 | .message-header {
5 | .button.icon-only .control-icon svg {
6 | fill: $white;
7 | }
8 | }
9 |
10 | &__license {
11 | font-size: $size-8;
12 | text-align: right;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Admin Manage.
5 | */
6 | export default defineMessages({
7 | manageHeader: {
8 | id: 'Admin.manage.header',
9 | defaultMessage: "Create & Manage",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/Bulma/LabeledProgressBar.scss:
--------------------------------------------------------------------------------
1 | @import '../../variables.scss';
2 |
3 | .labeled-progress {
4 | .description {
5 | display: flex;
6 | justify-content: space-between;
7 |
8 | .progress-made {
9 | .value {
10 | font-weight: $weight-semibold;
11 | }
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ProjectOverview/ProjectOverview.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .project-list {
4 | .project-overview {
5 | &__description {
6 | font-size: $size-6;
7 | margin-bottom: 20px;
8 | }
9 |
10 | &__controls {
11 | margin-top: 20px;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/CommentList/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CommentList
5 | */
6 | export default defineMessages({
7 | viewTaskLabel: {
8 | id: "CommentList.controls.viewTask.label",
9 | defaultMessage: "View Task"
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/MenuControlDivider.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | /**
4 | * Renders a divider between MenuControls in a grid block
5 | */
6 | export default class MenuControlDivider extends Component {
7 | render() {
8 | return
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/StatusRadarBlock/StatusRadarBlock.scss:
--------------------------------------------------------------------------------
1 | .status-radar-block {
2 | .completion-radar-chart {
3 | margin: 0;
4 | min-width: 200px;
5 | height: 350px;
6 |
7 | div {
8 | max-height: 100%;
9 | }
10 |
11 | svg {
12 | max-height: 345px;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ReviewTask/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ReviewTask
5 | */
6 | export default defineMessages({
7 | reviewTask: {
8 | id: "Admin.ReviewTask.header",
9 | defaultMessage: "Review Tasks",
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/AutosuggestTextBox/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with AutosuggestTextBox
5 | */
6 | export default defineMessages({
7 | noResults: {
8 | id: "AutosuggestTextBox.labels.noResults",
9 | defaultMessage: "No matches"
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/LoadMoreButton/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with LoadMoreButton.
5 | */
6 | export default defineMessages({
7 | moreResultsLabel: {
8 | id: "General.controls.moreResults.label",
9 | defaultMessage: "More Results",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlockPicker/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with GridBlockPicker
5 | */
6 | export default defineMessages({
7 | pickerLabel: {
8 | id: "GridBlockPicker.menuLabel",
9 | defaultMessage: "Add Widget",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/UserProfile/TopChallenges/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TopChallenges
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "UserProfile.topChallenges.header",
9 | defaultMessage: "Your Top Challenges",
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/EnhancedMap/FitBoundsControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with FitBoundsControl
5 | */
6 | export default defineMessages({
7 | tooltip: {
8 | id: "FitBoundsControl.tooltip",
9 | defaultMessage: "Fit map to task features"
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/TaskPane/MobileTaskDetails/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with MobileTaskDetails.
5 | */
6 | export default defineMessages({
7 | instructions: {
8 | id: 'MobileTask.subheading.instructions',
9 | defaultMessage: 'Instructions',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/CalendarHeatmap/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CalendarHeatmap.
5 | */
6 | export default defineMessages({
7 | heading: {
8 | id: "CalendarHeatmap.heading",
9 | defaultMessage: "Daily Heatmap: Task Completion",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/CompletionRadar/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CompletionRadar.
5 | */
6 | export default defineMessages({
7 | heading: {
8 | id: "CompletionRadar.heading",
9 | defaultMessage: "Tasks Completed: {taskCount, number}",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/MetricsOverview/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with MetricsOverview
5 | */
6 | export default defineMessages({
7 | evaluatedLabel: {
8 | id: "Metrics.tasks.evaluatedByUser.label",
9 | defaultMessage: "Tasks Evaluated by Users",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithErrors/__snapshots__/WithErrors.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`mapStateToProps maps currentErrors to errors 1`] = `
4 | Object {
5 | "errors": Array [
6 | Object {
7 | "defaultMessage": "A foo bar baz error.",
8 | "id": "Errors.foo.bar.baz",
9 | },
10 | ],
11 | }
12 | `;
13 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskRandomnessControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskRandomnessControl.
5 | */
6 | export default defineMessages({
7 | taskLoadByLabel: {
8 | id: 'Challenge.controls.taskLoadBy.label',
9 | defaultMessage: "Load tasks by:",
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/Bulma/Steps.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .steps:not(.is-hollow) {
4 | .steps-segment {
5 | .steps-marker.clickable:not(.is-hollow) {
6 | &:hover {
7 | cursor: pointer;
8 | box-shadow: 0px 2px 6px 0px $box-shadow-color;
9 | transition: box-shadow .25s ease-in-out;
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/ChallengeSearchMap/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeSearchMap
5 | */
6 | export default defineMessages({
7 | startChallengeLabel: {
8 | id: "ChallengeSearchMap.controls.startChallenge.label",
9 | defaultMessage: "Start Challenge"
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow, render, mount } from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | // React 16 Enzyme adapter
5 | Enzyme.configure({ adapter: new Adapter() })
6 |
7 | // Make Enzyme functions available in all test files without importing
8 | global.shallow = shallow
9 | global.render = render
10 | global.mount = mount
11 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCancelEditingControl/TaskCancelEditingControl.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../../variables.scss';
2 |
3 | .active-task-details .cancel-control {
4 | margin-top: 15px;
5 | margin-bottom: 15px;
6 |
7 | svg {
8 | height: 18px;
9 | width: auto;
10 | vertical-align: middle;
11 | margin-right: 10px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/TaskPane/VirtualChallengeNameLink/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with VirtualChallengeNameLink
5 | */
6 | export default defineMessages({
7 | virtualChallengeTooltip: {
8 | id: 'ActiveTask.indicators.virtualChallenge.tooltip',
9 | defaultMessage: 'Virtual Challenge',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/KeywordAutosuggestInput/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with KeywordAutosuggestInput
5 | */
6 | export default defineMessages({
7 | addKeywordPlaceholder: {
8 | id: "KeywordAutosuggestInput.controls.addKeyword.placeholder",
9 | defaultMessage: "Add keyword"
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithProjects/__snapshots__/WithProjects.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`mapStateToProps maps projects 1`] = `
4 | Object {
5 | "projects": Array [
6 | Object {
7 | "id": "123",
8 | "name": "first",
9 | },
10 | Object {
11 | "id": "456",
12 | "name": "second",
13 | },
14 | ],
15 | }
16 | `;
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/MoreOptionsControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with MoreOptionsControls
5 | */
6 | export default defineMessages({
7 | moreOptionsLabel: {
8 | id: "Task.controls.moreOptions.label",
9 | defaultMessage: "More Options"
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCommentInput/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskCommentInput.
5 | */
6 | export default defineMessages({
7 | placeholder: {
8 | id: 'Task.controls.completionComment.placeholder',
9 | defaultMessage: "Optional comment",
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/Ribbon/Ribbon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import classNames from 'classnames'
3 | import './Ribbon.css'
4 |
5 | export default class Ribbon extends Component {
6 | render() {
7 | return (
8 |
9 | {this.props.children}
10 |
11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | body {
4 | margin: 0;
5 | padding: 0;
6 | }
7 |
8 | .loading {
9 | display: flex;
10 | align-items: center;
11 | height: 100vh;
12 | background-color: $green;
13 | color: $white;
14 |
15 | svg {
16 | fill: $white;
17 | }
18 |
19 | h1.title {
20 | color: $white;
21 | }
22 |
23 | &__content {
24 | flex: auto;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/CalendarHeatmapBlock/CalendarHeatmapBlock.scss:
--------------------------------------------------------------------------------
1 | .calendar-heatmap-block {
2 | &.months-3 {
3 | .calendar-heatmap {
4 | max-width: 250px;
5 | }
6 | }
7 |
8 | &.months-6 {
9 | .calendar-heatmap {
10 | max-width: 450px;
11 | }
12 | }
13 |
14 | &.months-9 {
15 | .calendar-heatmap {
16 | max-width: 650px;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/PastDurationSelector/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with PastDurationSelector
5 | */
6 | export default defineMessages({
7 | pastMonthsOption: {
8 | id: "PastDurationSelector.pastMonths.selectOption",
9 | defaultMessage: "Past {months, plural, one {Month} =12 {Year} other {# Months}}",
10 | },
11 | })
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeType/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeType.
5 | */
6 | export default defineMessages({
7 | challenge: {
8 | id: "Challenge.type.challenge",
9 | defaultMessage: "Challenge",
10 | },
11 | survey: {
12 | id: "Challenge.type.survey",
13 | defaultMessage: "Survey",
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskLatLon/TaskLatLon.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .task-lat-lon {
4 |
5 | &__label {
6 | margin-right: 0.5em;
7 | }
8 |
9 | button.button.task-lat-lon__copy-button {
10 | height: 1em;
11 | margin-left: 0.5em;
12 |
13 | svg {
14 | fill: $green;
15 | }
16 |
17 | &:hover {
18 | svg {
19 | fill: $black;
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/services/Task/TaskLoadMethod/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskLoadMethod
5 | */
6 | export default defineMessages({
7 | random: {
8 | id: 'Task.loadByMethod.random',
9 | defaultMessage: "Random",
10 | },
11 |
12 | proximity: {
13 | id: 'Task.loadByMethod.proximity',
14 | defaultMessage: "Proximity",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/ChallengeProgress/ChallengeProgress.scss:
--------------------------------------------------------------------------------
1 | @import '../../variables.scss';
2 |
3 | .challenge-task-progress {
4 | margin-bottom: 20px;
5 | font-size: $size-9;
6 | height: 55px;
7 | position: relative;
8 |
9 | &__completed-indicator {
10 | position: absolute;
11 | top: 3px;
12 | right: 2px;
13 | fill: $green;
14 | stroke: $white;
15 | height: 20px;
16 | width: auto;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/ChallengeProgress/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeProgress
5 | */
6 | export default defineMessages({
7 | available: {
8 | id: "Task.fauxStatus.available",
9 | defaultMessage: "Available"
10 | },
11 |
12 | tooltipLabel: {
13 | id: "ChallengeProgress.tooltip.label",
14 | defaultMessage: "Tasks"
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/Navbar/AccountNavItem/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with AccountNavItem.
5 | */
6 | export default defineMessages({
7 | profile: {
8 | id: 'Navbar.links.userProfile',
9 | defaultMessage: "User Profile",
10 | },
11 |
12 | signout: {
13 | id: 'Navbar.links.signout',
14 | defaultMessage: "Sign out",
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/UserProfile/SavedTasks/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with SavedTasks.
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "UserProfile.savedTasks.header",
9 | defaultMessage: "Tracked Tasks",
10 | },
11 | unsave: {
12 | id: "Task.unsave.control.tooltip",
13 | defaultMessage: "Stop Tracking",
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/InsetMap/InsetMap.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import InsetMap from './InsetMap'
3 |
4 | const basicProps = {
5 | centerPoint: {
6 | lat: 0,
7 | lng: 0,
8 | },
9 | fixedZoom: 15,
10 | }
11 |
12 | test('renders an inset map at the given centerpoint and zoom', () => {
13 | const wrapper = shallow(
14 |
15 | )
16 |
17 | expect(wrapper.find('Map').exists()).toBe(true)
18 | })
19 |
--------------------------------------------------------------------------------
/src/components/UserProfile/SavedChallenges/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with SavedChallenges.
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "UserProfile.savedChallenges.header",
9 | defaultMessage: "Saved Challenges",
10 | },
11 | unsave: {
12 | id: "Challenge.controls.unsave.tooltip",
13 | defaultMessage: "Unsave Challenge",
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/src/services/Dashboard/ChallengeFilter/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeFilter
5 | */
6 | export default defineMessages({
7 | visible: {
8 | id: "Dashboard.ChallengeFilter.visible.label",
9 | defaultMessage: "Visible",
10 | },
11 |
12 | pinned: {
13 | id: "Dashboard.ChallengeFilter.pinned.label",
14 | defaultMessage: "Pinned",
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ManageTasks/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ManageTasks
5 | */
6 | export default defineMessages({
7 | editTaskTooltip: {
8 | id: "Admin.Task.controls.editTask.tooltip",
9 | defaultMessage: "Edit Task",
10 | },
11 |
12 | editTaskLabel: {
13 | id: "Admin.Task.controls.editTask.label",
14 | defaultMessage: "Edit",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskTrackControls/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskTrackControls
5 | */
6 | export default defineMessages({
7 | trackLabel: {
8 | id: "Task.controls.track.label",
9 | defaultMessage: "Track this Task",
10 | },
11 | untrackLabel: {
12 | id: "Task.controls.untrack.label",
13 | defaultMessage: "Stop tracking this Task",
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/BurndownChart/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with BurndownChart.
5 | */
6 | export default defineMessages({
7 | heading: {
8 | id: "BurndownChart.heading",
9 | defaultMessage: "Tasks Remaining: {taskCount, number}",
10 | },
11 |
12 | tooltip: {
13 | id: "BurndownChart.tooltip",
14 | defaultMessage: "Tasks Remaining",
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ChallengeTasksBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeTasksBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.ChallengeTasksBlock.label",
9 | defaultMessage: "Tasks",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.ChallengeTasksBlock.title",
14 | defaultMessage: "Tasks",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/LeaderboardBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with LeaderboardBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.LeaderboardBlock.label",
9 | defaultMessage: "Leaderboard",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.LeaderboardBlock.title",
14 | defaultMessage: "Leaderboard",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithLayerSources/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with WithLayerSources
5 | */
6 | export default defineMessages({
7 | challengeDefault: {
8 | id: "LayerSource.challengeDefault.label",
9 | defaultMessage: "Challenge Default",
10 | },
11 | userDefault: {
12 | id: "LayerSource.userDefault.label",
13 | defaultMessage: "Your Default",
14 | },
15 | })
16 |
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskEditControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskEditControl.
5 | */
6 | export default defineMessages({
7 | editLabel: {
8 | id: 'Task.controls.edit.label',
9 | defaultMessage: 'Edit',
10 | },
11 |
12 | editTooltip: {
13 | id: 'Task.controls.edit.tooltip',
14 | defaultMessage: 'Edit',
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskNextControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskNextControl.
5 | */
6 | export default defineMessages({
7 | nextLabel: {
8 | id: 'Task.controls.next.label',
9 | defaultMessage: 'Next Task',
10 | },
11 | nextTooltip: {
12 | id: 'Task.controls.next.tooltip',
13 | defaultMessage: 'Next Task',
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskSkipControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskSkipControl.
5 | */
6 | export default defineMessages({
7 | skipLabel: {
8 | id: 'Task.controls.skip.label',
9 | defaultMessage: 'Skip',
10 | },
11 | skipTooltip: {
12 | id: 'Task.controls.skip.tooltip',
13 | defaultMessage: 'Skip Task',
14 | },
15 | })
16 |
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskManageControls/TaskManageControls.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .active-task-controls__task-manage-controls {
4 | text-align: center;
5 |
6 | h3 {
7 | color: $coral;
8 | margin-top: 15px;
9 | font-weight: $weight-semibold;
10 | }
11 |
12 | &__options {
13 | a:not(:last-child) {
14 | border-right: 2px solid $grey-lightest;
15 | padding-right: 8px;
16 | margin-right: 8px;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/MenuControl.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | /**
4 | * Wraps a grid block control with the appropriate CSS classes to ensure it
5 | * displays properly in the block's dropdown menu
6 | */
7 | export default class MenuControl extends Component {
8 | render() {
9 | return(
10 |
11 | {this.props.children}
12 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectCountBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectCountBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.ProjectCountBlock.label",
9 | defaultMessage: "Project Count",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.ProjectCountBlock.title",
14 | defaultMessage: "Project Count",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectOverviewBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectOverviewBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.ProjectOverviewBlock.label",
9 | defaultMessage: "Overview",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.ProjectOverviewBlock.title",
14 | defaultMessage: "Overview",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/services/Task/TaskPriority/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskPriority.
5 | */
6 | export default defineMessages({
7 | high: {
8 | id: "Task.priority.high",
9 | defaultMessage: "High"
10 | },
11 | medium: {
12 | id: "Task.priority.medium",
13 | defaultMessage: "Medium"
14 | },
15 | low: {
16 | id: "Task.priority.low",
17 | defaultMessage: "Low"
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskNextControl/TaskNextControl.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../../variables.scss';
2 |
3 | .active-task-details .next-control {
4 | margin-top: 5px;
5 | margin-bottom: 10px;
6 |
7 | svg {
8 | height: 18px;
9 | width: auto;
10 | vertical-align: middle;
11 | margin-left: 10px;
12 | }
13 |
14 | &.icon-only {
15 | margin-top: 0;
16 |
17 | svg {
18 | margin-left: 0;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeList/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeList
5 | */
6 | export default defineMessages({
7 | addChallengeLabel: {
8 | id: 'Admin.Project.controls.addChallenge.label',
9 | defaultMessage: "Add Challenge",
10 | },
11 |
12 | noChallenges: {
13 | id: "Admin.ChallengeList.noChallenges",
14 | defaultMessage: "No Challenges",
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/RecentActivityBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with RecentActivityBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.RecentActivityBlock.label",
9 | defaultMessage: "Recent Activity",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.RecentActivityBlock.title",
14 | defaultMessage: "Recent Activity",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/StatusRadarBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with StatusRadarBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.StatusRadarBlock.label",
9 | defaultMessage: "Status Radar",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.StatusRadarBlock.title",
14 | defaultMessage: "Completion Status Distribution",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFixedControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskFixedControl.
5 | */
6 | export default defineMessages({
7 | fixedLabel: {
8 | id: 'Task.controls.fixed.label',
9 | defaultMessage: "I fixed it!",
10 | },
11 |
12 | fixedTooltip: {
13 | id: 'Task.controls.fixed.tooltip',
14 | defaultMessage: 'I fixed it!',
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/OwnerContactLink/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with OwnerContactLink
5 | */
6 | export default defineMessages({
7 | contactOwnerLabel: {
8 | id: 'Task.controls.contactOwner.label',
9 | defaultMessage: 'Contact Owner',
10 | },
11 |
12 | contactLinkLabel: {
13 | id: 'Task.controls.contactLink.label',
14 | defaultMessage: 'Message {owner} through OSM',
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/ChallengePane/ChallengeFilterSubnav/OtherKeywordsOption.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import SearchBox from '../../SearchBox/SearchBox'
3 |
4 | export default class OtherKeywordsOption extends Component {
5 | render() {
6 | return (
7 |
8 |
9 |
10 |
11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ReviewTaskControls/ReviewTaskControls.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../variables.scss';
2 |
3 | .review-task-controls {
4 | &__control-block {
5 | display: flex;
6 | justify-content: space-between;
7 |
8 | .button {
9 | margin-bottom: 10px;
10 | }
11 | }
12 |
13 | .review-task-controls__task-comment {
14 | margin-bottom: 20px;
15 | }
16 |
17 | button.post-comment-control {
18 | margin: -5px 0px 15px 10px;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/services/Project/GroupType/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with GroupType
5 | */
6 | export default defineMessages({
7 | admin: {
8 | id: "Project.GroupType.admin",
9 | defaultMessage: "Admin",
10 | },
11 | write: {
12 | id: "Project.GroupType.write",
13 | defaultMessage: "Write",
14 | },
15 | read: {
16 | id: "Project.GroupType.read",
17 | defaultMessage: "Read",
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/features/pages/Page.js:
--------------------------------------------------------------------------------
1 | export class Page {
2 | constructor() {
3 | this.title = "MR3 Page"
4 | }
5 |
6 | baseURL() {
7 | return process.env["chimp.mr3URL"]
8 | }
9 |
10 | get app() {
11 | return browser.element(".App")
12 | }
13 |
14 | appIsVisible() {
15 | this.app.isExisting()
16 | }
17 |
18 | waitForApp() {
19 | this.app.waitForVisible(10000)
20 | }
21 |
22 | open(path) {
23 | browser.url(this.baseURL() + path)
24 | this.waitForApp()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/BurndownChartBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with BurndownChartBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.BurndownChartBlock.label",
9 | defaultMessage: "Burndown Chart",
10 | },
11 | title: {
12 | id: "GridBlocks.BurndownChartBlock.title",
13 | defaultMessage: "Tasks Remaining: {taskCount, number}",
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/CalendarHeatmapBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CalendarHeatmapBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.CalendarHeatmapBlock.label",
9 | defaultMessage: "Daily Heatmap",
10 | },
11 | title: {
12 | id: "GridBlocks.CalendarHeatmapBlock.title",
13 | defaultMessage: "Daily Heatmap: Task Completion",
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ProjectDashboard/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectDashboard
5 | */
6 | export default defineMessages({
7 | editProjectLabel: {
8 | id: "Admin.ProjectDashboard.controls.edit.label",
9 | defaultMessage: "Edit",
10 | },
11 |
12 | addChallengeLabel: {
13 | id: "Admin.ProjectDashboard.controls.addChallenge.label",
14 | defaultMessage: "Add Challenge",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/TaskUploadingProgress/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskUploadingProgress
5 | */
6 | export default defineMessages({
7 | creatingTasks: {
8 | id: 'Admin.TaskUploadProgress.uploadingTasks.header',
9 | defaultMessage: "Rebuilding Tasks",
10 | },
11 |
12 | tasksCreated: {
13 | id: 'Admin.TaskUploadProgress.tasksUploaded.label',
14 | defaultMessage: "tasks uploaded",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/KeyboardShortcutReference/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with KeyboardShortcutReference.
5 | */
6 | export default defineMessages({
7 | control: {
8 | id: "KeyboardShortcuts.control.label",
9 | defaultMessage: "Keyboard Shortcuts",
10 | },
11 |
12 | keyboardShortcuts: {
13 | id: 'ActiveTask.keyboardShortcuts.label',
14 | defaultMessage: 'View Keyboard Shortcuts',
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AutosuggestTextBox/AutosuggestTextBox.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .autosuggest-text-box {
4 | width: 100%;
5 |
6 | &__input-wrapper {
7 | position: relative;
8 | width: 100%;
9 |
10 | input {
11 | padding-right: 40px;
12 | }
13 |
14 | .busy-spinner {
15 | position: absolute;
16 | top: 8px;
17 | right: 5px;
18 | }
19 | }
20 |
21 | &__no-results {
22 | font-size: $size-7;
23 | margin: 0 1em;
24 | color: $grey-light;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCancelEditingControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskCancelEditingControl.
5 | */
6 | export default defineMessages({
7 | cancelEditingLabel: {
8 | id: 'Task.controls.cancelEditing.label',
9 | defaultMessage: "Go Back",
10 | },
11 | cancelEditingTooltip: {
12 | id: 'Task.controls.cancelEditing.tooltip',
13 | defaultMessage: "Go Back",
14 | },
15 | })
16 |
17 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/TaskCompletionStep2.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../../variables.scss';
2 |
3 | .task-completion-controls {
4 | display: flex;
5 | flex-direction: column;
6 |
7 | button {
8 | margin-bottom: 10px;
9 | }
10 |
11 | &__cancel {
12 | margin-top: 15px;
13 | margin-bottom: 15px;
14 |
15 | svg {
16 | height: 18px;
17 | width: auto;
18 | vertical-align: middle;
19 | margin-right: 10px;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskAlreadyFixedControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskAlreadyFixedControl.
5 | */
6 | export default defineMessages({
7 | alreadyFixedLabel: {
8 | id: 'Task.controls.alreadyFixed.label',
9 | defaultMessage: "Already fixed",
10 | },
11 |
12 | alreadyFixedTooltip: {
13 | id: 'Task.controls.alreadyFixed.tooltip',
14 | defaultMessage: "Already fixed",
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.scss:
--------------------------------------------------------------------------------
1 | .edit-challenge {
2 | position: relative;
3 | min-width: 500px;
4 |
5 | ul.steps {
6 | position: absolute;
7 | right: -30px;
8 |
9 | @media(max-width: 750px) {
10 | position: static;
11 | &:not(.is-vertical).is-narrow.is-right {
12 | justify-content: center;
13 | }
14 | }
15 | }
16 |
17 | .rjsf .array-field .priority-rule {
18 | input[type=text] {
19 | max-width: 150px;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Bulma/Menu.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | aside.menu {
4 | ul.menu-list {
5 | li {
6 | position: relative;
7 |
8 | svg.menu-list__active-indicator {
9 | fill: $green;
10 | width: 0.5em;
11 | height: auto;
12 | position: absolute;
13 | top: 13px;
14 | left: 5px;
15 | }
16 |
17 | a {
18 | padding: 0.5em 0.75em 0.5em 1.25em;
19 | }
20 |
21 | label {
22 | color: $grey-dark;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskTooHardControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskTooHardControl.
5 | */
6 | export default defineMessages({
7 | tooHardLabel: {
8 | id: 'Task.controls.tooHard.label',
9 | defaultMessage: "Too difficult / Couldn't see",
10 | },
11 |
12 | tooHardTooltip: {
13 | id: 'Task.controls.tooHard.tooltip',
14 | defaultMessage: "Too difficult / Couldn't see",
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFalsePositiveControl/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskFalsePositiveControl.
5 | */
6 | export default defineMessages({
7 | falsePositiveLabel: {
8 | id: 'Task.controls.falsePositive.label',
9 | defaultMessage: 'Not an Issue',
10 | },
11 |
12 | falsePositiveTooltip: {
13 | id: 'Task.controls.falsePositive.tooltip',
14 | defaultMessage: 'Not an Issue',
15 | },
16 | })
17 |
18 |
--------------------------------------------------------------------------------
/src/interactions/Overpass/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Overpass interactions
5 | */
6 | export default defineMessages({
7 | noOverpassTurboShortcuts: {
8 | id: "Admin.EditChallenge.overpass.errors.noTurboShortcuts",
9 | defaultMessage: "Overpass Turbo shortcuts are not supported. If you wish to use them, please visit Overpass Turbo and test your query, then choose Export -> Query -> Standalone -> Copy and then paste that here.",
10 | },
11 | })
12 |
13 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ProjectLeaderboard/ProjectLeaderboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Leaderboard from '../../../Leaderboard/Leaderboard'
3 |
4 | export default class ProjectLeaderboard extends Component {
5 | render() {
6 | return
11 | }
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/components/PageNotFound/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with PageNotFound
5 | */
6 | export default defineMessages({
7 | missingPage: {
8 | id: "PageNotFound.message",
9 | defaultMessage: "Sorry, nothing here but open ocean.",
10 | },
11 |
12 | returnTo: {
13 | id: "PageNotFound.returnTo",
14 | defaultMessage: "Return to",
15 | },
16 |
17 | homePage: {
18 | id: "PageNotFound.homePage",
19 | defaultMessage: "home page",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskRandomnessControl/TaskRandomnessControl.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .task-randomness-control {
4 | margin: 15px 0px;
5 |
6 | .control {
7 | display: flex;
8 | justify-content: center;
9 | flex-wrap: wrap;
10 | }
11 |
12 | &__prompt {
13 | margin-right: 0.5em;
14 | }
15 |
16 | &__options {
17 | display: flex;
18 | flex-direction: column;
19 | justify-content: flex-start;
20 | align-items: center;
21 |
22 | input {
23 | margin: 0 5px;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/ScreenTooNarrow/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ScreenTooNarrow
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "ScreenTooNarrow.header",
9 | defaultMessage: "Please widen your browser window"
10 | },
11 |
12 | message: {
13 | id: "ScreenTooNarrow.message",
14 | defaultMessage: "This page is not yet compatible with smaller screens. Please expand your browser window or switch to a larger device or display."
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeOwnerLeaderboard/ChallengeOwnerLeaderboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Leaderboard from '../../../Leaderboard/Leaderboard'
3 |
4 | export default class ChallengeOwnerLeaderboard extends Component {
5 | render() {
6 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/Dashboard/ProjectFilter/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectFilter
5 | */
6 | export default defineMessages({
7 | visible: {
8 | id: "Dashboard.ProjectFilter.visible.label",
9 | defaultMessage: "Visible",
10 | },
11 |
12 | owner: {
13 | id: "Dashboard.ProjectFilter.owner.label",
14 | defaultMessage: "Owned",
15 | },
16 |
17 | pinned: {
18 | id: "Dashboard.ProjectFilter.pinned.label",
19 | defaultMessage: "Pinned",
20 | },
21 | })
22 |
23 |
--------------------------------------------------------------------------------
/src/services/Task/TaskAction/TaskAction.js:
--------------------------------------------------------------------------------
1 | import { TaskStatus } from '../TaskStatus/TaskStatus'
2 | import _fromPairs from 'lodash/fromPairs'
3 | import _map from 'lodash/map'
4 | import _keys from 'lodash/keys'
5 |
6 | /**
7 | * Returns an actions object with everything zeroed-out to represent that there
8 | * are no actions.
9 | */
10 | export const zeroTaskActions = function() {
11 | const actions =
12 | _fromPairs(_map(_keys(TaskStatus), statusName => [statusName, 0]))
13 | actions.total = 0
14 | actions.available = 0
15 |
16 | return actions
17 | }
18 |
--------------------------------------------------------------------------------
/src/services/Server/RequestCache.js:
--------------------------------------------------------------------------------
1 | import Cache from 'stale-lru-cache'
2 |
3 | /**
4 | * The primary purpose of the cache is simply to reduce duplicate requests to
5 | * the serverf that can naturally occur during a single user action from
6 | * decoupled components that don't know anything about data other components
7 | * might already have retrieved. So the maxAge of the cache is set very low.
8 | */
9 | export const cache = new Cache({
10 | maxSize: 100,
11 | maxAge: 10, // seconds
12 | })
13 |
14 | export const resetCache = () => {
15 | cache.reset()
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithCurrentUser/__snapshots__/WithCurrentUser.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`mapStateToProps adds isLoggedIn and isSuperUser fields to the user 1`] = `
4 | Object {
5 | "user": Object {
6 | "id": 456,
7 | "isLoggedIn": true,
8 | "isSuperUser": true,
9 | },
10 | }
11 | `;
12 |
13 | exports[`mapStateToProps provides the current user in denormalized form 1`] = `
14 | Object {
15 | "user": Object {
16 | "id": 456,
17 | "isLoggedIn": undefined,
18 | "isSuperUser": undefined,
19 | },
20 | }
21 | `;
22 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskManageControls/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskManagementControls.
5 | */
6 | export default defineMessages({
7 | heading: {
8 | id: "Task.management.heading",
9 | defaultMessage: "Management Options",
10 | },
11 | reviewLabel: {
12 | id: "Task.management.controls.review.label",
13 | defaultMessage: "Review",
14 | },
15 | modifyLabel: {
16 | id: "Task.management.controls.modify.label",
17 | defaultMessage: "Modify",
18 | },
19 | })
20 |
21 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/CommentsBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CommentsBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.CommentsBlock.label",
9 | defaultMessage: "Comments",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.CommentsBlock.title",
14 | defaultMessage: "Comments",
15 | },
16 |
17 | exportLabel: {
18 | id: "GridBlocks.CommentsBlock.controls.export.label",
19 | defaultMessage: "Export",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/CongratulateModal/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CongratulateModal.
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "CongratulateModal.header",
9 | defaultMessage: "Congratulations!"
10 | },
11 |
12 | primaryMessage: {
13 | id: "CongratulateModal.primaryMessage",
14 | defaultMessage: "Challenge is complete"
15 | },
16 |
17 | dismiss: {
18 | id: "CongratulateModal.control.dismiss.label",
19 | defaultMessage: "Continue"
20 | },
21 | })
22 |
23 |
--------------------------------------------------------------------------------
/src/services/Search/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Sort.
5 | */
6 | export default defineMessages({
7 | name: {
8 | id: "Challenge.sort.name",
9 | defaultMessage: "Name",
10 | },
11 | created: {
12 | id: "Challenge.sort.created",
13 | defaultMessage: "Newest",
14 | },
15 | popularity: {
16 | id: "Challenge.sort.popularity",
17 | defaultMessage: "Popular",
18 | },
19 | default: {
20 | id: "Challenge.sort.default",
21 | defaultMessage: "Smart",
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectOverviewBlock/ProjectOverviewBlock.scss:
--------------------------------------------------------------------------------
1 | .project-overview-block {
2 | .grid-block__content {
3 | .columns {
4 | margin: -0.5em 0;
5 |
6 | &:first-child {
7 | margin-top: 0;
8 | }
9 |
10 | &:last-child {
11 | margin-bottom: 0;
12 | }
13 |
14 | .column {
15 | padding: 0.25em;
16 | }
17 | }
18 | }
19 |
20 | .project-overview__status.status-section {
21 | padding-top: 5px;
22 | }
23 |
24 | &__project-description {
25 | margin-top: 20px;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /src/**/*.css
6 |
7 | # Imagery
8 | src/imagery.json
9 | src/defaultLayers.json
10 | src/customLayers.json
11 |
12 | # testing
13 | /coverage
14 | chimp.js
15 |
16 | # production
17 | /build
18 | /MR3React.tgz
19 |
20 | # misc
21 | .DS_Store
22 | .env.local
23 | .env.development
24 | .env.development.local
25 | .env.test
26 | .env.test.local
27 | .env.production
28 | .env.production.local
29 |
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 |
34 | *.swp
35 |
--------------------------------------------------------------------------------
/src/components/ConfirmAction/ConfirmAction.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .confirm-action {
4 | display: inline-flex;
5 | justify-content: flex-start;
6 |
7 | .message .message-body {
8 | background-color: $white;
9 | }
10 |
11 | &__prompt {
12 | color: $grey-dark;
13 | font-size: $size-5;
14 | }
15 |
16 | &__controls {
17 | margin-top: 20px;
18 | display: flex;
19 | justify-content: flex-end;
20 |
21 | button.button {
22 | margin-right: 15px;
23 |
24 | &:last-child {
25 | margin-right: 0;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectListBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectListBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.ProjectListBlock.label",
9 | defaultMessage: "Project List",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.ProjectListBlock.title",
14 | defaultMessage: "Projects",
15 | },
16 |
17 | searchPlaceholder: {
18 | id: "GridBlocks.ProjectListBlock.search.placeholder",
19 | defaultMessage: "Search",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ChallengeListBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeListBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.ChallengeListBlock.label",
9 | defaultMessage: "Challenges",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.ChallengeListBlock.title",
14 | defaultMessage: "Challenges",
15 | },
16 |
17 | searchPlaceholder: {
18 | id: "GridBlocks.ChallengeListBlock.search.placeholder",
19 | defaultMessage: "Search",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithStatus/WithStatus.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import _get from 'lodash/get'
3 | import { FETCHING_CHALLENGES_STATUS,
4 | CHECKING_LOGIN_STATUS }
5 | from '../../../services/Status/Status'
6 |
7 | export const mapStateToProps = state => {
8 | return {
9 | fetchingChallenges:
10 | _get(state, `currentStatus.${FETCHING_CHALLENGES_STATUS}`, []),
11 |
12 | checkingLoginStatus:
13 | _get(state, `currentStatus.${CHECKING_LOGIN_STATUS}`, false),
14 | }
15 | }
16 |
17 | export default WrappedComponent =>
18 | connect(mapStateToProps)(WrappedComponent)
19 |
--------------------------------------------------------------------------------
/src/components/QuickTextBox/QuickTextBox.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .quick-text-box {
4 | display: flex;
5 | justify-content: space-between;
6 | align-items: center;
7 |
8 | .control-wrapper {
9 | margin-right: 10px;
10 | flex: 1 1 100%;
11 | }
12 |
13 | button.button {
14 | height: 45px;
15 | margin-left: 5px;
16 |
17 | &.is-small {
18 | height: 32px;
19 | }
20 | }
21 |
22 | &__done-button.button {
23 | svg {
24 | fill: $green;
25 | }
26 | }
27 |
28 | &__cancel-button.button {
29 | svg {
30 | fill: $coral;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ProjectOverview/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ProjectOverview
5 | */
6 | export default defineMessages({
7 | creationDate: {
8 | id: "Admin.Project.fields.creationDate.label",
9 | defaultMessage: "Created:",
10 | },
11 |
12 | lastModifiedDate: {
13 | id: "Admin.Project.fields.lastModifiedDate.label",
14 | defaultMessage: "Modified:",
15 | },
16 |
17 | deleteProject: {
18 | id: "Admin.Project.controls.delete.label",
19 | defaultMessage: "Delete Project",
20 | },
21 | })
22 |
23 |
--------------------------------------------------------------------------------
/src/components/HelpPopout/HelpPopout.scss:
--------------------------------------------------------------------------------
1 | @import '../../variables.scss';
2 |
3 | .help-popout {
4 | &.dropdown.popout-right {
5 | .menu-wrapper {
6 | position: absolute;
7 | left: 45px;
8 | }
9 | }
10 |
11 | &--control {
12 | margin: 0 5px;
13 |
14 | svg {
15 | fill: $grey-light;
16 | width: 20px;
17 | height: auto;
18 | }
19 |
20 | &:hover {
21 | svg {
22 | fill: $grey-dark;
23 | }
24 | }
25 | }
26 |
27 | &--content {
28 | min-width: 300px;
29 | padding: 10px;
30 | color: $grey-darkest;
31 | font-size: $size-6;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/TaskStatusIndicator/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskStatusIndicator
5 | */
6 | export default defineMessages({
7 | statusPrompt: {
8 | id: 'ActiveTask.subheading.status',
9 | defaultMessage: "Existing Status",
10 | },
11 |
12 | statusTooltip: {
13 | id: 'ActiveTask.controls.status.tooltip',
14 | defaultMessage: "Existing Status",
15 | },
16 |
17 | viewChangeset: {
18 | id: 'ActiveTask.controls.viewChangset.label',
19 | defaultMessage: "View Changeset",
20 | }
21 | })
22 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeDifficulty/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeDifficulty.
5 | */
6 | export default defineMessages({
7 | easy: {
8 | id: "Challenge.difficulty.easy",
9 | defaultMessage: "Easy",
10 | },
11 | normal: {
12 | id: "Challenge.difficulty.normal",
13 | defaultMessage: "Normal",
14 | },
15 | expert: {
16 | id: "Challenge.difficulty.expert",
17 | defaultMessage: "Expert",
18 | },
19 | any: {
20 | id: "Challenge.difficulty.any",
21 | defaultMessage: "Any",
22 | }
23 | })
24 |
25 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithCurrentTask/__snapshots__/WithCurrentTask.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`mapDispatchToProps maps some functions 1`] = `
4 | Object {
5 | "completeTask": [Function],
6 | "fetchOSMUser": [Function],
7 | "loadTask": [Function],
8 | "nextTask": [Function],
9 | "refreshTask": [Function],
10 | }
11 | `;
12 |
13 | exports[`mapStateToProps maps task from the taskId in the route 1`] = `
14 | Object {
15 | "challengeId": undefined,
16 | "task": Object {
17 | "id": 987,
18 | "name": "task987",
19 | "parent": 123,
20 | },
21 | "taskId": 987,
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/src/components/PastDurationSelector/PastDurationSelector.scss:
--------------------------------------------------------------------------------
1 | @import 'theme.scss';
2 |
3 | .past-duration-selector {
4 | .dropdown-trigger {
5 | line-height: 24px;
6 |
7 | .button.is-outlined {
8 | @include invert-on-hover($grey-light, $white);
9 | font-weight: $weight-normal;
10 |
11 | .dropdown-indicator {
12 | @include arrow($grey-light);
13 | height: 10px;
14 | width: 10px;
15 | position: relative;
16 | margin-left: 0.5em;
17 | bottom: 2px;
18 | }
19 | }
20 | }
21 |
22 | .dropdown-item {
23 | font-weight: $weight-normal;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCommentInput/TaskCommentInput.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../../variables.scss';
2 |
3 | .task-completion-comment {
4 | width: 100%;
5 |
6 | input {
7 | width: 100%;
8 | font-size: $size-6;
9 | color: $grey;
10 | padding: 8px 12px;
11 | border: 2px solid $grey;
12 | border-radius: $radius-medium;
13 |
14 | &::placeholder {
15 | color: $grey;
16 | font-size: $size-7;
17 | }
18 |
19 | &:focus {
20 | outline: 0 !important;
21 | box-shadow: none !important;
22 | border-color: $green;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Bulma/RJSFFormFieldAdapter/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with RJSFFormFieldAdapter.
5 | */
6 | export default defineMessages({
7 | uploadFilePrompt: {
8 | id: "Form.textUpload.prompt",
9 | defaultMessage: "Drop GeoJSON file here or click to select file",
10 | },
11 |
12 | readOnlyFile: {
13 | id: "Form.textUpload.readonly",
14 | defaultMessage: "Existing file will be used",
15 | },
16 |
17 | addPriorityRuleLabel: {
18 | id: "Form.controls.addPriorityRule.label",
19 | defaultMessage: "Add a Rule",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ChallengeShareControls/ChallengeShareControls.scss:
--------------------------------------------------------------------------------
1 | .challenge-share-controls {
2 | display: flex;
3 | flex-direction: row;
4 | align-items: center;
5 | justify-content: flex-start;
6 | flex-wrap: wrap;
7 | overflow: hidden;
8 |
9 | .share-control {
10 | margin-right: 15px;
11 |
12 | &:hover {
13 | cursor: pointer;
14 | }
15 |
16 | .SocialMediaShareButton:focus {
17 | outline: 0;
18 | }
19 | }
20 |
21 | &.is-minimized {
22 | flex-direction: column;
23 |
24 | .share-control {
25 | margin-right: 0;
26 | margin-bottom: 15px;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/TaskPane/TaskLocationMap/TaskLocationMap.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .task-location-map {
4 | position: relative;
5 | border: 2px solid $grey-lighter;
6 | border-radius: $radius;
7 |
8 | &__extent-map {
9 | position: absolute;
10 | top: -20px;
11 | right: -15px;
12 | height:90px;
13 | width: 90px;
14 | box-shadow: -4px 5px 20px -6px $grey-darkest;
15 | z-index: $intralayer-bump;
16 | border: 2px solid $grey-lighter;
17 | border-radius: $radius;
18 |
19 | .leaflet-container {
20 | height: 100%;
21 | border-radius: 0 0 $radius-medium 0;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ProjectListBlock/ProjectListBlock.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .admin .project-list-block {
4 | .grid-block__header__title-row {
5 | }
6 |
7 | .grid-block__header__title-row__title {
8 | h2.subtitle {
9 | margin-top: 8px;
10 | margin-right: 30px;
11 | }
12 | }
13 |
14 | &__searchbox.search-box {
15 | max-width: 250px;
16 | margin: 10px 20px 10px 0;
17 | background-color: $white;
18 |
19 | .control-wrapper {
20 | width: 100%;
21 | }
22 | }
23 |
24 | .project-list {
25 | display: flex;
26 | justify-content: center;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/ConfirmAction/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ConfirmAction.
5 | */
6 | export default defineMessages({
7 | title: {
8 | id: "ConfirmAction.title",
9 | defaultMessage: "Please Confirm",
10 | },
11 |
12 | prompt: {
13 | id: "ConfirmAction.prompt",
14 | defaultMessage: "Are you sure? This cannot be undone.",
15 | },
16 |
17 | cancel: {
18 | id: "ConfirmAction.cancel",
19 | defaultMessage: "Cancel",
20 | },
21 |
22 | proceed: {
23 | id: "ConfirmAction.proceed",
24 | defaultMessage: "Proceed",
25 | },
26 | })
27 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeKeywords/ChallengeKeywords.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ChallengeKeywords from './ChallengeKeywords'
3 |
4 | let basicProps = null
5 |
6 | beforeEach(() => {
7 | basicProps = {
8 | challenge: {
9 | id: 123,
10 | tags: ["foo", "bar", "baz"],
11 | }
12 | }
13 | })
14 |
15 | test("renders the challenge tags", () => {
16 | const wrapper = shallow(
17 |
18 | )
19 |
20 | expect(
21 | wrapper.find('.challenge-keywords .tag').length
22 | ).toBe(basicProps.challenge.tags.length)
23 |
24 | expect(wrapper).toMatchSnapshot()
25 | })
26 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/CompletionProgressBlock/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with CompletionProgressBlock
5 | */
6 | export default defineMessages({
7 | label: {
8 | id: "GridBlocks.CompletionProgressBlock.label",
9 | defaultMessage: "Completion Progress",
10 | },
11 |
12 | title: {
13 | id: "GridBlocks.CompletionProgressBlock.title",
14 | defaultMessage: "Completion Progress",
15 | },
16 |
17 | noTasks: {
18 | id: "GridBlocks.CompletionProgressBlock.noTasks",
19 | defaultMessage: "Challenge has no tasks",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/components/SvgSymbol/SvgSymbol.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SvgSymbol from './SvgSymbol'
3 |
4 | test('it renders a reference to the given svg', () => {
5 | const wrapper = shallow(
6 |
7 | )
8 |
9 | expect(wrapper.find('use[xlinkHref="#foo"]').exists()).toBe(true)
10 | expect(wrapper).toMatchSnapshot()
11 | })
12 |
13 | test('it includes the given viewbox', () => {
14 | const wrapper = shallow(
15 |
16 | )
17 |
18 | expect(wrapper.find('svg[viewBox="0 0 20 20"]').exists()).toBe(true)
19 | expect(wrapper).toMatchSnapshot()
20 | })
21 |
--------------------------------------------------------------------------------
/src/interactions/Project/AsManageableProject.js:
--------------------------------------------------------------------------------
1 | import _filter from 'lodash/filter'
2 | import _isObject from 'lodash/isObject'
3 |
4 | /**
5 | * AsManageable adds functionality to a Project related to management.
6 | */
7 | export class AsManageableProject {
8 | constructor(project) {
9 | Object.assign(this, project)
10 | }
11 |
12 | childChallenges(challenges) {
13 | return _filter(challenges, challenge =>
14 | _isObject(challenge.parent) ? challenge.parent.id === this.id :
15 | challenge.parent === this.id
16 | )
17 | }
18 | }
19 |
20 | export default project => new AsManageableProject(project)
21 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/Dashboard/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Dashboard
5 | */
6 | export default defineMessages({
7 | renameConfigurationTooltip: {
8 | id: "Dashboard.controls.renameConfiguration.tooltip",
9 | defaultMessage: "Rename layout",
10 | },
11 |
12 | addConfigurationTooltip: {
13 | id: "Dashboard.controls.addConfiguration.tooltip",
14 | defaultMessage: "Add new layout",
15 | },
16 |
17 | deleteConfigurationTooltip: {
18 | id: "Dashboard.controls.deleteConfiguration.tooltip",
19 | defaultMessage: "Delete layout",
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeZoom/ChallengeZoom.js:
--------------------------------------------------------------------------------
1 | import _range from 'lodash/range'
2 |
3 | /**
4 | * Constants related to zoom levels. These are based on
5 | * OpenStreetMap zoom levels.
6 | *
7 | * @see See https://wiki.openstreetmap.org/wiki/Zoom_levels
8 | *
9 | * @author [Neil Rotstan](https://github.com/nrotstan)
10 | */
11 |
12 | /** Minimum possible zoom */
13 | export const MIN_ZOOM = 0
14 |
15 | /** Maximum possible zoom */
16 | export const MAX_ZOOM = 19
17 |
18 | /** Default zoom level */
19 | export const DEFAULT_ZOOM = 13
20 |
21 | /** Array of all zoom levels */
22 | export const ZOOM_LEVELS = Object.freeze(_range(MIN_ZOOM, MAX_ZOOM + 1))
23 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ProjectsDashboard/ProjectsDashboard.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .admin__manage.projects-dashboard {
4 | &__dashboard-controls {
5 | display: flex;
6 | justify-content: flex-end;
7 | }
8 |
9 | .projects-dashboard__no-projects {
10 | margin: 40px 15px 15px 15px;
11 | padding: 15px;
12 | color: $primary;
13 | background-color: $white;
14 | font-size: $size-5;
15 | border-radius: $radius-medium;
16 | text-align: center;
17 | }
18 |
19 | .heading-name {
20 | transform-origin: left;
21 | transform: rotate(0deg) translate(0px, 0px);
22 | transition: transform 0.5s;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/DashboardFilterToggle/DashboardFilterToggle.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | const DashboardFilterToggle = (filterType, filterName) => {
4 | return class extends Component {
5 | render() {
6 | return (
7 |
8 | this.props.toggleEntityFilter(filterName)} /> {this.props.filterToggleLabel}
11 |
12 | )
13 | }
14 | }
15 | }
16 |
17 | export default DashboardFilterToggle
18 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/StepNavigation/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with StepNavigation
5 | */
6 | export default defineMessages({
7 | cancel: {
8 | id: "StepNavigation.controls.cancel.label",
9 | defaultMessage: "Cancel",
10 | },
11 |
12 | next: {
13 | id: "StepNavigation.controls.next.label",
14 | defaultMessage: "Next",
15 | },
16 |
17 | prev: {
18 | id: "StepNavigation.controls.prev.label",
19 | defaultMessage: "Prev",
20 | },
21 |
22 | finish: {
23 | id: "StepNavigation.controls.finish.label",
24 | defaultMessage: "Finish",
25 | },
26 | })
27 |
28 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithErrors/WithErrors.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { addError, removeError, clearErrors } from '../../../services/Error/Error'
3 |
4 | export const mapStateToProps = (state, ownProps) => {
5 | return ({errors: state.currentErrors})
6 | }
7 |
8 | export const mapDispatchToProps = dispatch => {
9 | return {
10 | addError: error => dispatch(addError(error)),
11 | removeError: error => dispatch(removeError(error)),
12 | clearErrors: () => dispatch(clearErrors()),
13 | }
14 | }
15 |
16 | const WithErrors =
17 | WrappedComponent => connect(mapStateToProps, mapDispatchToProps)(WrappedComponent)
18 |
19 | export default WithErrors
20 |
--------------------------------------------------------------------------------
/src/components/ProjectLeaderboard/ProjectLeaderboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Leaderboard from '../Leaderboard/Leaderboard'
3 | import WithProject from '../HOCs/WithProject/WithProject'
4 | import _get from 'lodash/get'
5 |
6 | export class ProjectLeaderboard extends Component {
7 | render() {
8 | return
12 | }
13 | }
14 |
15 | export default WithProject(ProjectLeaderboard)
16 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithTaskCenterPoint/WithTaskCenterPoint.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import AsMappableTask from '../../../interactions/Task/AsMappableTask'
4 |
5 | export default function(WrappedComponent) {
6 | class WithTaskCenterPoint extends Component {
7 | render() {
8 | const mappableTask = AsMappableTask(this.props.task)
9 | return
11 | }
12 | }
13 |
14 | WithTaskCenterPoint.propTypes = {
15 | task: PropTypes.object.isRequired,
16 | }
17 |
18 | return WithTaskCenterPoint
19 | }
20 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeLocation/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeLocation
5 | */
6 | export default defineMessages({
7 | nearMe: {
8 | id: "Challenge.location.nearMe",
9 | defaultMessage: "Near Me",
10 | },
11 | withinMapBounds: {
12 | id: "Challenge.location.withinMapBounds",
13 | defaultMessage: "Within Map Bounds",
14 | },
15 | intersectingMapBounds: {
16 | id: "Challenge.location.intersectingMapBounds",
17 | defaultMessage: "Intersecting Map Bounds",
18 | },
19 | any: {
20 | id: "Challenge.location.any",
21 | defaultMessage: "Anywhere",
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/ChallengePane/ChallengeResultList/SortChallengesSelector.scss:
--------------------------------------------------------------------------------
1 | @import 'theme.scss';
2 |
3 | .sort-challenges-selector {
4 | .dropdown-trigger {
5 | line-height: 24px;
6 |
7 | .button {
8 | min-width: 110px;
9 | }
10 |
11 | .button.is-outlined {
12 | @include invert-on-hover($grey-light, $white);
13 | font-weight: $weight-normal;
14 |
15 | .dropdown-indicator {
16 | @include arrow($grey-light);
17 | height: 10px;
18 | width: 10px;
19 | position: relative;
20 | margin-left: 0.5em;
21 | bottom: 2px;
22 | }
23 | }
24 | }
25 |
26 | .dropdown-item {
27 | font-weight: $weight-normal;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/MobileFilterMenu/MobileFilterMenu.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .mobile-filter-menu {
4 | padding: 8px 10px;
5 |
6 | .dropdown-trigger {
7 | margin-top: 3px;
8 | }
9 |
10 | &__label {
11 | position: relative;
12 | color: $blue;
13 | text-transform: uppercase;
14 | font-size: $size-8;
15 | font-weight: $weight-medium;
16 | letter-spacing: $letter-spacing-medium;
17 | }
18 |
19 | aside.menu {
20 | width: 250px;
21 | max-height: 65vh;
22 | overflow-y: auto;
23 |
24 | .challenge_filter_subnav__other-keywords {
25 | padding: 6px 0.75em 6px 1.25em;
26 |
27 | .search-box button.is-clear {
28 | height: 20px;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/EnhancedMap/LayerToggle/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with LayerToggle
5 | */
6 | export default defineMessages({
7 | showTaskFeaturesLabel: {
8 | id: "LayerToggle.controls.showTaskFeatures.label",
9 | defaultMessage: "Task Features"
10 | },
11 |
12 | showMapillaryLabel: {
13 | id: "LayerToggle.controls.showMapillary.label",
14 | defaultMessage: "Mapillary"
15 | },
16 |
17 | imageCount: {
18 | id: "LayerToggle.imageCount",
19 | defaultMessage: "({count, plural, =0 {no images} other {# images}})"
20 | },
21 |
22 | loading: {
23 | id: "LayerToggle.loading",
24 | defaultMessage: "(loading...)"
25 | },
26 | })
27 |
--------------------------------------------------------------------------------
/src/components/CommentList/CommentCountBadge/CommentCountBadge.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CommentCountBadge from './CommentCountBadge'
3 |
4 | test('it renders a badge with the count of the given comments', () => {
5 | const wrapper = shallow(
6 |
7 | )
8 |
9 | expect(wrapper.find('.badge[data-badge=3]').exists()).toBe(true)
10 | expect(wrapper).toMatchSnapshot()
11 | })
12 |
13 | test('it renders a badge with is-empty class for empty comments', () => {
14 | const wrapper = shallow(
15 |
16 | )
17 |
18 | expect(wrapper.find('.badge.is-empty[data-badge=0]').exists()).toBe(true)
19 | expect(wrapper).toMatchSnapshot()
20 | })
21 |
--------------------------------------------------------------------------------
/src/components/ChallengeLeaderboard/ChallengeLeaderboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Leaderboard from '../Leaderboard/Leaderboard'
3 | import WithChallenge from '../HOCs/WithChallenge/WithChallenge'
4 | import _get from 'lodash/get'
5 |
6 | export class ChallengeLeaderboard extends Component {
7 | render() {
8 | return
13 | }
14 | }
15 |
16 | export default WithChallenge(ChallengeLeaderboard)
17 |
--------------------------------------------------------------------------------
/src/components/ChallengePane/ChallengeFilterSubnav/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeFilterSubnav
5 | */
6 | export default defineMessages({
7 | difficultyLabel: {
8 | id: 'ChallengeFilterSubnav.filter.difficulty.label',
9 | defaultMessage: 'Difficulty',
10 | },
11 | keywordLabel: {
12 | id: 'ChallengeFilterSubnav.filter.keyword.label',
13 | defaultMessage: 'Work on',
14 | },
15 | locationLabel: {
16 | id: 'ChallengeFilterSubnav.filter.location.label',
17 | defaultMessage: 'Location',
18 | },
19 | searchLabel: {
20 | id: 'ChallengeFilterSubnav.filter.search.label',
21 | defaultMessage: 'Search for a challenge...',
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskCompletionStep2
5 | */
6 | export default defineMessages({
7 | fixed: {
8 | id: 'ActiveTask.controls.fixed.label',
9 | defaultMessage: "I fixed it!",
10 | },
11 |
12 | notFixed: {
13 | id: 'ActiveTask.controls.notFixed.label',
14 | defaultMessage: "Too difficult / Couldn't see",
15 | },
16 |
17 | alreadyFixed: {
18 | id: 'ActiveTask.controls.aleadyFixed.label',
19 | defaultMessage: "Already fixed",
20 | },
21 |
22 | cancelEditing: {
23 | id: 'ActiveTask.controls.cancelEditing.label',
24 | defaultMessage: "Go Back",
25 | }
26 | })
27 |
--------------------------------------------------------------------------------
/src/services/User/Locale/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Locale
5 | */
6 | export default defineMessages({
7 | 'en-US': {
8 | id: "Locale.en-US.label",
9 | defaultMessage: "en-US (U.S. English)",
10 | },
11 | es: {
12 | id: "Locale.es.label",
13 | defaultMessage: "es (Español)",
14 | },
15 | de: {
16 | id: "Locale.de.label",
17 | defaultMessage: "de (Deutsch)",
18 | },
19 | fr: {
20 | id: "Locale.fr.label",
21 | defaultMessage: "fr (Français)",
22 | },
23 | af: {
24 | id: "Locale.af.label",
25 | defaultMessage: "af (Afrikaans)",
26 | },
27 | ja: {
28 | id: "Locale.ja.label",
29 | defaultMessage: "ja (日本語)",
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/features/pages/OpenStreetMap.js:
--------------------------------------------------------------------------------
1 | import { Page } from './Page'
2 |
3 | class OpenStreetMap extends Page {
4 | get username() {
5 | return browser.element("input#username")
6 | }
7 |
8 | get password() {
9 | return browser.element("input#password")
10 | }
11 |
12 | get loginButton() {
13 | return browser.element("#login_form input[type=submit]")
14 | }
15 |
16 | get authorizeButton() {
17 | return browser.element(".oauth-authorize form input[type=submit]")
18 | }
19 |
20 | waitForAuthorizationForm() {
21 | browser.waitForVisible(".oauth-authorize form", 10000)
22 | }
23 |
24 | open() {
25 | browser.url('https://www.openstreetmap.org/login')
26 | browser.waitForVisible("#login_form", 10000)
27 | }
28 | }
29 |
30 | export default new OpenStreetMap()
31 |
--------------------------------------------------------------------------------
/features/steps/HomePageSteps.js:
--------------------------------------------------------------------------------
1 | import HomePage from '../pages/HomePage'
2 |
3 | export default function() {
4 | this.Given(/^(\w+) visits (MapRoulette|the site|the home page)$/, function(username, pageName) {
5 | HomePage.open()
6 | })
7 |
8 | this.Given(/^(\w+) is browsing MapRoulette$/, function(username) {
9 | if (!HomePage.appIsVisible()) {
10 | HomePage.open()
11 | }
12 |
13 | HomePage.waitForKnownLoginStatus()
14 | HomePage.getStarted.click()
15 | })
16 |
17 | this.Given(/^(\w+) opens the Account nav menu$/, function(username) {
18 | HomePage.waitForKnownLoginStatus()
19 | HomePage.accountNavMenu.click()
20 | })
21 |
22 | this.Given(/^(\w+) clicks Get Started on the home page$/, function(user) {
23 | HomePage.getStarted.click()
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/Task/TaskLoadMethod/TaskLoadMethod.js:
--------------------------------------------------------------------------------
1 | import _fromPairs from 'lodash/fromPairs'
2 | import _map from 'lodash/map'
3 | import messages from './Messages'
4 |
5 | /** Load tasks randomly within challenge */
6 | export const RANDOM_LOAD_METHOD = 'random'
7 |
8 | /** Load tasks by proximity within challenge */
9 | export const PROXIMITY_LOAD_METHOD = 'proximity'
10 |
11 | export const TaskLoadMethod = Object.freeze({
12 | random: RANDOM_LOAD_METHOD,
13 | proximity: PROXIMITY_LOAD_METHOD,
14 | })
15 |
16 | /**
17 | * Returns an object mapping status values to raw internationalized
18 | * messages suitable for use with FormattedMessage or formatMessage.
19 | */
20 | export const messagesByLoadMethod = _fromPairs(
21 | _map(messages, (message, key) => [TaskLoadMethod[key], message])
22 | )
23 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/Dashboard/Dashboard.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .dashboard {
4 | position: relative;
5 | background-color: $grey-lightest-more;
6 | border-radius: $radius-medium;
7 | margin-top: -20px;
8 |
9 | &__tab-row {
10 | background-color: $white;
11 | display: flex;
12 | justify-content: space-between;
13 |
14 | &__tabs {
15 | display: flex;
16 | justify-content: flex-start;
17 | }
18 |
19 | &__controls {
20 | display: flex;
21 | align-items: center;
22 |
23 | .button {
24 | margin-left: 15px;
25 | }
26 | }
27 | }
28 | }
29 |
30 | .admin .dashboard .tabs ul {
31 | border-bottom-style: none;
32 |
33 | li {
34 | &.is-active {
35 | margin-bottom: 0;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/KeywordAutosuggestInput/KeywordAutosuggestInput.scss:
--------------------------------------------------------------------------------
1 | @import 'mixins.scss';
2 |
3 | .keyword-autosuggest-input {
4 | @include basic-form-element-styling();
5 |
6 | > span { // wrapper around react-tagsinput
7 | display: flex;
8 | }
9 |
10 | .react-tagsinput-tag {
11 | @include tag();
12 | display: inline-flex;
13 | }
14 |
15 | .autosuggest-text-box__input-wrapper {
16 | input {
17 | border: none;
18 | box-shadow: none;
19 | }
20 | }
21 |
22 | .dropdown-menu {
23 | margin-bottom: 10px;
24 |
25 | .dropdown-content {
26 | .new-keyword {
27 | padding-bottom: 11px;
28 | border-bottom: 2px solid $grey-lightest;
29 | margin-bottom: 5px;
30 | font-style: italic;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/interactions/Challenge/AsMappableChallenge.js:
--------------------------------------------------------------------------------
1 | import _isFinite from 'lodash/isFinite'
2 | import { basemapLayerSource }
3 | from '../../services/VisibleLayer/LayerSources'
4 |
5 | /**
6 | * AsMappableChallenge adds functionality to a Challenge related to mapping.
7 | */
8 | export class AsMappableChallenge {
9 | constructor(challenge) {
10 | Object.assign(this, challenge)
11 | }
12 |
13 | defaultLayerSource() {
14 | if (!_isFinite(this.id)) {
15 | return null
16 | }
17 |
18 | return basemapLayerSource(this.defaultBasemap,
19 | this.defaultBasemapId,
20 | this.customBasemap,
21 | `challenge_${this.id}`)
22 | }
23 | }
24 |
25 | export default challenge => new AsMappableChallenge(challenge)
26 |
--------------------------------------------------------------------------------
/src/components/CommentList/CommentCountBadge/CommentCountBadge.scss:
--------------------------------------------------------------------------------
1 | @import '../../../variables.scss';
2 |
3 | .comment-count-badge {
4 | display: inline-block;
5 |
6 | .badge.is-badge-outlined[data-badge] {
7 | font-size: 22px; // badge size and positioning affected by font size
8 | margin-left: 4px;
9 |
10 | svg {
11 | height: 19px;
12 | width: auto;
13 | fill: $grey;
14 | }
15 | }
16 |
17 | .badge.is-badge-outlined[data-badge]::after {
18 | font-size: $size-9;
19 | padding: 0.25rem;
20 | color: $white;
21 | background-color: $coral;
22 | border: none;
23 | left: calc(100% - ( 1.2rem / 2 ) - 1px) // move slightly to the left
24 | }
25 |
26 | .badge.is-badge-outlined.is-empty[data-badge]::after {
27 | background-color: $grey-light;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/services/Editor/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Editor
5 | */
6 | export default defineMessages({
7 | none: {
8 | id: "Editor.none.label",
9 | defaultMessage: "None"
10 | },
11 | id: {
12 | id: "Editor.id.label",
13 | defaultMessage: "Edit in iD (web editor)"
14 | },
15 | josm: {
16 | id: "Editor.josm.label",
17 | defaultMessage: "Edit in JOSM"
18 | },
19 | josmLayer: {
20 | id: "Editor.josmLayer.label",
21 | defaultMessage: "Edit in new JOSM layer"
22 | },
23 | josmFeatures: {
24 | id: "Editor.josmFeatures.label",
25 | defaultMessage: "Edit just features in JOSM"
26 | },
27 | level0: {
28 | id: "Editor.level0.label",
29 | defaultMessage: "Edit in Level0"
30 | }
31 | })
32 |
--------------------------------------------------------------------------------
/src/services/VisibleLayer/VisibleLayer.js:
--------------------------------------------------------------------------------
1 | // redux actions
2 | const CHANGE_VISIBLE_LAYER = 'ChangeVisibleLayer'
3 |
4 | // redux action creators
5 |
6 | /**
7 | * Set the given map tile layer as the current visible tile layer in the redux
8 | * store.
9 | *
10 | * @param {string} layerId - the layerId of the layer to set. It must correspond
11 | * to a valid layer source layerId.
12 | *
13 | * @see See VisibleLayer/LayerSources
14 | */
15 | export const changeVisibleLayer = function(layerId) {
16 | return {
17 | type: CHANGE_VISIBLE_LAYER,
18 | layerId,
19 | }
20 | }
21 |
22 | // redux reducers
23 | export const visibleLayer = function(state=null, action) {
24 | if (action.type === CHANGE_VISIBLE_LAYER) {
25 | return {id: action.layerId}
26 | }
27 | else {
28 | return state
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Navbar/AccountNavItem/AccountNavItem.scss:
--------------------------------------------------------------------------------
1 | @import '../../../theme.scss';
2 |
3 | .navbar__account-nav-item {
4 | .dropdown-trigger {
5 | display: flex;
6 | justify-content: flex-start;
7 | align-items: center;
8 |
9 | &:hover {
10 | cursor: pointer;
11 | }
12 | }
13 |
14 | .dropdown-menu {
15 | z-index: $layer-dropdown + $intralayer-bump;
16 | }
17 |
18 | &__avatar {
19 | margin-right: 10px;
20 |
21 | .circular-image {
22 | @include circular-image();
23 | }
24 | }
25 |
26 | &__username {
27 | font-weight: $weight-normal;
28 | margin-right: 10px;
29 | }
30 |
31 | &__icon {
32 | @include arrow($white);
33 | transform: rotate(-45deg);
34 | transform-origin: center;
35 | transition: transform 0.25s;
36 | position: static;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeAnalysisTable/ChallengeAnalysisTable.scss:
--------------------------------------------------------------------------------
1 | @import 'theme.scss';
2 |
3 | .challenge-analysis-table {
4 | @include styled-react-table()
5 | margin-bottom: 40px;
6 |
7 | &__column-controls {
8 | font-size: $size-5;
9 | margin-bottom: 15px;
10 |
11 | input {
12 | font-size: $size-5;
13 | }
14 | }
15 |
16 | .completed-column {
17 | text-align: center;
18 |
19 | .cell-icon {
20 | fill: $green;
21 | }
22 | }
23 |
24 | .challenge-task-progress {
25 | margin-bottom: 0;
26 | }
27 |
28 | .rt-td.challenge-actions-column {
29 | overflow: inherit;
30 | text-align: right;
31 |
32 | .challenge-actions-column__action-label {
33 | display: flex;
34 | }
35 |
36 | .menu-wrapper {
37 | text-align: left;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithStatus/__snapshots__/WithStatus.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`checkingLoginStatus returns false if no status 1`] = `
4 | Object {
5 | "checkingLoginStatus": false,
6 | "fetchingChallenges": Array [],
7 | }
8 | `;
9 |
10 | exports[`fetchingChallenges returns empty array if no status 1`] = `
11 | Object {
12 | "checkingLoginStatus": false,
13 | "fetchingChallenges": Array [],
14 | }
15 | `;
16 |
17 | exports[`mapStateToProps maps checkingLoginStatus 1`] = `
18 | Object {
19 | "checkingLoginStatus": true,
20 | "fetchingChallenges": Array [],
21 | }
22 | `;
23 |
24 | exports[`mapStateToProps maps fetchingChallenges 1`] = `
25 | Object {
26 | "checkingLoginStatus": false,
27 | "fetchingChallenges": Array [
28 | "fetch123",
29 | "fetch456",
30 | ],
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeStatus/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeStatus.
5 | */
6 | export default defineMessages({
7 | none: {
8 | id: "Challenge.status.none",
9 | defaultMessage: "Not Applicable"
10 | },
11 | building: {
12 | id: "Challenge.status.building",
13 | defaultMessage: "Building"
14 | },
15 | failed: {
16 | id: "Challenge.status.failed",
17 | defaultMessage: "Failed"
18 | },
19 | ready: {
20 | id: "Challenge.status.ready",
21 | defaultMessage: "Ready"
22 | },
23 | partiallyLoaded: {
24 | id: "Challenge.status.partiallyLoaded",
25 | defaultMessage: "Partially Loaded"
26 | },
27 | finished: {
28 | id: "Challenge.status.finished",
29 | defaultMessage: "Finished"
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/src/components/Navbar/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with Navbar.
5 | */
6 | export default defineMessages({
7 | results: {
8 | id: 'Navbar.links.challengeResults',
9 | defaultMessage: "Challenges",
10 | },
11 |
12 | leaderboard: {
13 | id: 'Navbar.links.leaderboard',
14 | defaultMessage: "Leaderboard",
15 | },
16 |
17 | adminCreate: {
18 | id: 'Navbar.links.admin',
19 | defaultMessage: "Create",
20 | },
21 |
22 | help: {
23 | id: 'Navbar.links.help',
24 | defaultMessage: "Help",
25 | },
26 |
27 | profile: {
28 | id: 'Navbar.links.mobile.userProfile',
29 | defaultMessage: "User Profile",
30 | },
31 |
32 | signout: {
33 | id: 'Navbar.mobile.links.signout',
34 | defaultMessage: "Sign out",
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/src/interactions/User/AsMappingUser.js:
--------------------------------------------------------------------------------
1 | import _isFinite from 'lodash/isFinite'
2 | import _isObject from 'lodash/isObject'
3 | import { basemapLayerSource }
4 | from '../../services/VisibleLayer/LayerSources'
5 |
6 | /**
7 | * AsMappingUser adds functionality to a user related to mapping.
8 | */
9 | export class AsMappingUser {
10 | constructor(user) {
11 | Object.assign(this, user)
12 | }
13 |
14 | defaultLayerSource() {
15 | if (!_isFinite(this.id) || !_isObject(this.settings)) {
16 | return null
17 | }
18 |
19 | return basemapLayerSource(this.settings.defaultBasemap,
20 | this.settings.defaultBasemapId,
21 | this.settings.customBasemap,
22 | `user_${this.id}`)
23 | }
24 | }
25 |
26 | export default user => new AsMappingUser(user)
27 |
--------------------------------------------------------------------------------
/src/components/Bulma/TriStateCheckbox.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import _omit from 'lodash/omit'
3 |
4 | /**
5 | * Checkbox that supports `indeterminate` prop, allowing checkbox to be
6 | * checked, unchecked, or indeterminate.
7 | */
8 | export default class TriStateCheckbox extends Component {
9 | componentDidMount() {
10 | this.el.indeterminate = this.props.indeterminate
11 | }
12 |
13 | componentDidUpdate(prevProps) {
14 | if (prevProps.indeterminate !== this.props.indeterminate) {
15 | this.el.indeterminate = this.props.indeterminate
16 | }
17 | }
18 |
19 | render() {
20 | return (
21 | this.el = el} />
23 | )
24 | }
25 | }
26 |
27 | TriStateCheckbox.defaultProps = {
28 | indeterminate: false,
29 | }
30 |
--------------------------------------------------------------------------------
/features/Signin.feature:
--------------------------------------------------------------------------------
1 | Feature: Sign-in Feature
2 |
3 | Users sign in via OAuth by using their OpenStreetMap account and granting
4 | access to the MapRoulette app.
5 |
6 | Scenario: User signs in using OpenStreetMap account
7 | Given mr3testing/mr3testing logs in to OpenStreetMap
8 | And mr3testing is browsing MapRoulette
9 | And mr3testing is signed out from MapRoulette
10 | And mr3testing clicks the Sign In nav link
11 | And mr3testing authorizes the MapRoulette app on OpenStreetMap
12 | Then mr3testing should be signed in to MapRoulette
13 |
14 | Scenario: User signs out through the Account nav menu
15 | Given mr3testing is browsing MapRoulette
16 | And mr3testing/mr3testing is signed in to MapRoulette
17 | And mr3testing clicks Sign Out on the Account nav menu
18 | Then mr3testing should be signed out from MapRoulette
19 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeBasemap/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeBasemap.
5 | */
6 | export default defineMessages({
7 | none: {
8 | id: "Challenge.basemap.none",
9 | defaultMessage: "None",
10 | },
11 | noneChallengeOwner: {
12 | id: "Admin.Challenge.basemap.none",
13 | defaultMessage: "User Default",
14 | },
15 | openStreetMap: {
16 | id: "Challenge.basemap.openStreetMap",
17 | defaultMessage: "OpenStreetMap",
18 | },
19 | openCycleMap: {
20 | id: "Challenge.basemap.openCycleMap",
21 | defaultMessage: "OpenCycleMap",
22 | },
23 | bing: {
24 | id: "Challenge.basemap.bing",
25 | defaultMessage: "Bing",
26 | },
27 | custom: {
28 | id: "Challenge.basemap.custom",
29 | defaultMessage: "Custom",
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/src/components/EnhancedMap/LayerToggle/LayerToggle.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .layer-toggle.dropdown {
4 | position: absolute;
5 | z-index: $layer-map-control + $intralayer-bump;
6 | top: 10px;
7 | right: 10px;
8 |
9 | .dropdown-trigger > button.button:focus, .dropdown-trigger > button.button:hover {
10 | border-color: $grey-lighter;
11 | background-color: $grey-lightest-more;
12 | }
13 |
14 | .layer-toggle__icon {
15 | fill: $green;
16 | width: 30px;
17 | height: auto;
18 | stroke-width: 2px;
19 | }
20 |
21 | .layer-toggle__option-controls {
22 | div.checkbox {
23 | font-size: $size-7;
24 | padding-left: 1rem;
25 | color: $grey-dark;
26 |
27 | &:hover {
28 | color: $grey-dark;
29 | }
30 |
31 | label {
32 | padding-left: 10px;
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/MobileNotSupported/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with MobileNotSupported
5 | */
6 | export default defineMessages({
7 | header: {
8 | id: "MobileNotSupported.header",
9 | defaultMessage: "Please Visit on your Computer"
10 | },
11 |
12 | message: {
13 | id: "MobileNotSupported.message",
14 | defaultMessage: "Sorry, MapRoulette does not currently support mobile devices."
15 | },
16 |
17 | pageMessage: {
18 | id: "MobileNotSupported.pageMessage",
19 | defaultMessage: "Sorry, this page is not yet compatible with mobile devices and smaller screens."
20 | },
21 |
22 | widenDisplay: {
23 | id: "MobileNotSupported.widenDisplay",
24 | defaultMessage: "If using a computer, please widen your window or use a larger display."
25 | },
26 | })
27 |
28 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/ChallengeTaskMap/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ChallengeTaskMap
5 | */
6 | export default defineMessages({
7 | clusterTasksLabel: {
8 | id: "Admin.ChallengeTaskMap.controls.clusterTasks.label",
9 | defaultMessage: "Group Tasks",
10 | },
11 |
12 | reviewTaskLabel: {
13 | id: "Admin.ChallengeTaskMap.controls.reviewTask.label",
14 | defaultMessage: "Review Task",
15 | },
16 |
17 | editTaskLabel: {
18 | id: "Admin.ChallengeTaskMap.controls.editTask.label",
19 | defaultMessage: "Edit Task",
20 | },
21 |
22 | nameLabel: {
23 | id: "Admin.Task.fields.name.label",
24 | defaultMessage: "Task:",
25 | },
26 |
27 | statusLabel: {
28 | id: "Admin.Task.fields.status.label",
29 | defaultMessage: "Status:",
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/src/services/Activity/ActivityItemTypes/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ActivityItemType.
5 | */
6 | export default defineMessages({
7 | project: {
8 | id: "Activity.item.project",
9 | defaultMessage: "Project"
10 | },
11 | challenge: {
12 | id: "Activity.item.challenge",
13 | defaultMessage: "Challenge"
14 | },
15 | task: {
16 | id: "Activity.item.task",
17 | defaultMessage: "Task"
18 | },
19 | tag: {
20 | id: "Activity.item.tag",
21 | defaultMessage: "Tag"
22 | },
23 | survey: {
24 | id: "Activity.item.survey",
25 | defaultMessage: "Survey"
26 | },
27 | user: {
28 | id: "Activity.item.user",
29 | defaultMessage: "User"
30 | },
31 | group: {
32 | id: "Activity.item.group",
33 | defaultMessage: "Group"
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/components/ChallengePane/ChallengePane.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ChallengePane } from './ChallengePane'
3 | import { ChallengeDifficulty }
4 | from '../../services/Challenge/ChallengeDifficulty/ChallengeDifficulty'
5 |
6 | let challenge = null
7 | let basicProps = null
8 |
9 | beforeEach(() => {
10 | challenge = {
11 | id: 123,
12 | }
13 |
14 | basicProps = {
15 | user: {
16 | id: 11,
17 | savedChallenges: [],
18 | },
19 | startChallenge: jest.fn(),
20 | saveChallenge: jest.fn(),
21 | unsaveChallenge: jest.fn(),
22 | intl: {formatMessage: jest.fn()},
23 | }
24 | })
25 |
26 | test("renders with props as expected", () => {
27 | const wrapper = shallow(
28 |
29 | )
30 |
31 | expect(wrapper.find('.challenge-pane').exists()).toBe(true)
32 | expect(wrapper).toMatchSnapshot()
33 | })
34 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithEditor/WithEditor.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { editTask, closeEditor } from '../../../services/Editor/Editor'
3 |
4 | /**
5 | * WithEditor provides an editor prop to its WrappedComponent that contains the
6 | * current open editor (if any) from the redux store, as well as functions for
7 | * initiating and ending editing of a task.
8 | *
9 | * @author [Neil Rotstan](https://github.com/nrotstan)
10 | */
11 | const WithEditor =
12 | WrappedComponent => connect(mapStateToProps, mapDispatchToProps)(WrappedComponent)
13 |
14 | export const mapStateToProps = state => ({
15 | editor: state.openEditor,
16 | })
17 |
18 | export const mapDispatchToProps = dispatch => ({
19 | editTask: (editor, task, mapBounds) => dispatch(editTask(editor, task, mapBounds)),
20 | closeEditor: () => dispatch(closeEditor()),
21 | })
22 |
23 | export default WithEditor
24 |
--------------------------------------------------------------------------------
/src/components/TaskPane/ActiveTaskDetails/ReviewTaskControls/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with ReviewTaskControls
5 | */
6 | export default defineMessages({
7 | previousTaskLabel: {
8 | id: "Admin.TaskReview.controls.previousTask.label",
9 | defaultMessage: "Prior Task",
10 | },
11 | nextTaskLabel: {
12 | id: "Admin.TaskReview.controls.nextTask.label",
13 | defaultMessage: "Next Task",
14 | },
15 | editTaskLabel: {
16 | id: "Admin.TaskReview.controls.editTask.label",
17 | defaultMessage: "Edit Task",
18 | },
19 | modifyTaskLabel: {
20 | id: "Admin.TaskReview.controls.modifyTask.label",
21 | defaultMessage: "Modify Task Data",
22 | },
23 | postCommentLabel: {
24 | id: "Admin.TaskReview.controls.postTaskComment.label",
25 | defaultMessage: "Post",
26 | },
27 |
28 | })
29 |
--------------------------------------------------------------------------------
/src/components/UserProfile/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with UserProfile.
5 | */
6 | export default defineMessages({
7 | apiKey: {
8 | id: "UserProfile.apiKey.header",
9 | defaultMessage: "API Key",
10 | },
11 |
12 | apiKeyCopyLabel: {
13 | id: "UserProfile.apiKey.controls.copy.label",
14 | defaultMessage: "Copy",
15 | },
16 |
17 | apiKeyResetLabel: {
18 | id: "UserProfile.apiKey.controls.reset.label",
19 | defaultMessage: "Reset",
20 | },
21 |
22 | overviewTab: {
23 | id: "UserProfile.tabs.overview",
24 | defaultMessage: "Overview",
25 | },
26 |
27 | activityTab: {
28 | id: "UserProfile.tabs.activity",
29 | defaultMessage: "Activity",
30 | },
31 |
32 | settingsTab: {
33 | id: "UserProfile.tabs.settings",
34 | defaultMessage: "Settings",
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/src/services/Task/TaskStatus/Messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | /**
4 | * Internationalized messages for use with TaskStatus.
5 | */
6 | export default defineMessages({
7 | created: {
8 | id: "Task.status.created",
9 | defaultMessage: "Created"
10 | },
11 | fixed: {
12 | id: "Task.status.fixed",
13 | defaultMessage: "Fixed"
14 | },
15 | falsePositive: {
16 | id: "Task.status.falsePositive",
17 | defaultMessage: "Not an Issue"
18 | },
19 | skipped: {
20 | id: "Task.status.skipped",
21 | defaultMessage: "Skipped"
22 | },
23 | deleted: {
24 | id: "Task.status.deleted",
25 | defaultMessage: "Deleted"
26 | },
27 | alreadyFixed: {
28 | id: "Task.status.alreadyFixed",
29 | defaultMessage: "Already Fixed"
30 | },
31 | tooHard: {
32 | id: "Task.status.tooHard",
33 | defaultMessage: "Too Hard"
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/components/AdminPane/HOCs/WithWideScreenOption/WithWideScreenOption.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | /**
4 | * WithWideScreenOption passes down an isWideScreen prop to the wrapped component,
5 | * as well as a setWideScreen function for altering the isWideScreen prop.
6 | *
7 | * @author [Neil Rotstan](https://github.com/nrotstan)
8 | */
9 | export default function WithWideScreenOption(WrappedComponent, defaultToWide=false) {
10 | return class extends Component {
11 | state = {
12 | isWideScreen: defaultToWide
13 | }
14 |
15 | setWideScreen = (isWide=true) => {
16 | this.setState({isWideScreen: isWide})
17 | }
18 |
19 | render() {
20 | return
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Bulma/Menu.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import MenuList from './MenuList'
4 | import './Menu.css'
5 |
6 | /**
7 | * Menu renders a Bulma menu with the given menuLists
8 | */
9 | export default class Menu extends Component {
10 | render() {
11 | const menuLists = this.props.menuLists ?
12 | this.props.menuLists.map(menuConfig =>
13 |
17 | ) : this.props.children
18 |
19 | return (
20 |
21 | )
22 | }
23 | }
24 |
25 | Menu.propTypes = {
26 | menuLists: PropTypes.arrayOf(PropTypes.object),
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/Challenge/ChallengeType/ChallengeType.js:
--------------------------------------------------------------------------------
1 | import _map from 'lodash/map'
2 | import _fromPairs from 'lodash/fromPairs'
3 | import messages from './Messages'
4 |
5 | // These constants are defined on the server
6 | export const CHALLENGE_TYPE_CHALLENGE = 1
7 | export const CHALLENGE_TYPE_SURVEY = 4
8 |
9 | export const ChallengeType = Object.freeze({
10 | challenge: CHALLENGE_TYPE_CHALLENGE,
11 | survey: CHALLENGE_TYPE_SURVEY,
12 | })
13 |
14 | /**
15 | * Returns an object mapping difficulty values to raw internationalized
16 | * messages suitable for use with FormattedMessage or formatMessage.
17 | */
18 | export const messagesByType = _fromPairs(
19 | _map(messages, (message, key) => [ChallengeType[key], message])
20 | )
21 |
22 | /** Returns object containing localized labels */
23 | export const typeLabels = intl => _fromPairs(
24 | _map(messages, (message, key) => [key, intl.formatMessage(message)])
25 | )
26 |
--------------------------------------------------------------------------------
/src/components/ChallengeProgress/ChallengeProgress.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ChallengeProgress } from './ChallengeProgress'
3 | import _cloneDeep from 'lodash/cloneDeep'
4 |
5 | let basicProps = null
6 |
7 | beforeEach(() => {
8 | basicProps = {
9 | challenge: {
10 | id: 123,
11 | actions: {total: 5, available: 3, completed: 2}
12 | },
13 | intl: {formatMessage: jest.fn()},
14 | }
15 | })
16 |
17 | test("renders with props as expected", () => {
18 | const wrapper = shallow(
19 |
20 | )
21 |
22 | expect(wrapper.find('.challenge-task-progress').exists()).toBe(true)
23 | expect(wrapper).toMatchSnapshot()
24 | })
25 |
26 | test("does not explode with null challenge", () => {
27 | delete basicProps.challenge
28 | const wrapper = shallow(
29 |
30 | )
31 |
32 | expect(wrapper).toMatchSnapshot()
33 | })
34 |
--------------------------------------------------------------------------------
/src/components/ErrorModal/ErrorModal.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ErrorModal } from './ErrorModal'
3 |
4 | let basicProps = null
5 |
6 | beforeEach(() => {
7 | basicProps = {
8 | errors: [
9 | {
10 | id: "first-error",
11 | defaultMessage: "First Error"
12 | },
13 | {
14 | id: "second-error",
15 | defaultMessage: "Second Error"
16 | },
17 | {
18 | id: "third-error",
19 | defaultMessage: "Third Error"
20 | },
21 | ],
22 | removeError: jest.fn(),
23 | clearErrors: jest.fn(),
24 | formatMessage: jest.fn((params) => params.defaultMessage),
25 | }
26 | })
27 |
28 | test('it renders a list of the given errors', () => {
29 | const wrapper = shallow(
30 |
31 | )
32 |
33 | expect(wrapper.find('li').length).toBe(basicProps.errors.length)
34 |
35 | expect(wrapper).toMatchSnapshot()
36 | })
37 |
--------------------------------------------------------------------------------
/src/interactions/Overpass/AsValidatableOverpass.js:
--------------------------------------------------------------------------------
1 | import messages from './Messages'
2 |
3 | const overpassTurboShortcutRegex = /{{2}[^}]+}{2}/
4 |
5 | /**
6 | * Provides methods related to validating Overpass queries.
7 | *
8 | * @author [Neil Rotstan](https://github.com/nrotstan)
9 | */
10 | export class AsValidatableOverpass {
11 | constructor(overpassQuery) {
12 | this.overpassQuery = overpassQuery
13 | }
14 |
15 | /**
16 | * Validate the raw Overpass query. This just looks for a few basic problems,
17 | * such as inclusion of Overpass Turbo query shortcuts (mustache tags).
18 | */
19 | validate() {
20 | const errors = []
21 |
22 | if (overpassTurboShortcutRegex.test(this.overpassQuery)) {
23 | errors.push({
24 | message: messages.noOverpassTurboShortcuts
25 | })
26 | }
27 |
28 | return errors
29 | }
30 | }
31 |
32 | export default overpassQuery => new AsValidatableOverpass(overpassQuery)
33 |
--------------------------------------------------------------------------------
/src/components/Bulma/SvgControl.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import classNames from 'classnames'
4 | import SvgSymbol from '../SvgSymbol/SvgSymbol'
5 |
6 | /**
7 | * SvgControl renders a simple clickable control consisting solely of the SVG
8 | * referenced by the sym prop.
9 | */
10 | export default class SvgControl extends Component {
11 | render() {
12 | return (
13 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 | }
22 |
23 | SvgControl.propTypes = {
24 | sym: PropTypes.string.isRequired,
25 | viewBox: PropTypes.string,
26 | onClick: PropTypes.func,
27 | }
28 |
29 | SvgControl.defaultProps = {
30 | viewBox: "0 0 20 20",
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/CalendarHeatmap/CalendarHeatmap.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .calendar-heatmap {
4 | $calendar-bucket-colors: (
5 | $chart-blue-light, $chart-green, $chart-brown,
6 | $chart-yellow, $chart-orange, $chart-red
7 | );
8 |
9 | @for $i from 1 through length($calendar-bucket-colors) {
10 | .color-bucket-#{$i - 1} { fill: nth($calendar-bucket-colors, $i); }
11 | }
12 |
13 | .color-empty {
14 | fill: $grey-lightest-more;
15 | }
16 |
17 | .react-calendar-heatmap {
18 | margin-top: 10px;
19 |
20 | text {
21 | font-size: 10px;
22 | fill: #aaa;
23 | }
24 |
25 | .react-calendar-heatmap-small-text {
26 | font-size: 5px;
27 | }
28 | }
29 |
30 | &.vertical {
31 | .react-calendar-heatmap {
32 | width: 140px;
33 | height: 800px;
34 | }
35 | }
36 |
37 | &.high-contrast {
38 | .color-empty {
39 | fill: $white;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/GridBlocks/ChallengeOverviewBlock/ChallengeOverviewBlock.scss:
--------------------------------------------------------------------------------
1 | @import 'variables.scss';
2 |
3 | .challenge-overview-block {
4 | .grid-block__content {
5 | .columns {
6 | margin: -0.5em 0;
7 |
8 | &:first-child {
9 | margin-top: 0;
10 | }
11 |
12 | &:last-child {
13 | margin-bottom: 0;
14 | }
15 |
16 | .column {
17 | padding: 0.25em;
18 | }
19 | }
20 |
21 | .status-label {
22 | font-weight: $weight-light;
23 | color: $grey;
24 | font-size: $size-6-plus;
25 | }
26 |
27 | .status-value {
28 | color: $grey;
29 | font-size: $size-6-plus;
30 | }
31 |
32 | .challenge-keywords {
33 | margin-top: 15px;
34 | margin-bottom: 20px;
35 |
36 | .tag {
37 | margin-bottom: 5px;
38 | }
39 | }
40 |
41 | .start-challenge-control {
42 | margin-bottom: 20px;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/HOCs/WithProjects/WithProjects.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { denormalize } from 'normalizr'
3 | import _compact from 'lodash/compact'
4 | import _map from 'lodash/map'
5 | import _get from 'lodash/get'
6 | import { projectSchema } from '../../../services/Project/Project'
7 | import { fetchProjectChallenges } from '../../../services/Challenge/Challenge'
8 |
9 | export const mapStateToProps = (state, ownProps) => ({
10 | projects: _compact(_map(_get(state, 'entities.projects', {}),
11 | project => project.id ?
12 | denormalize(project, projectSchema(), state.entities) :
13 | null))
14 | })
15 |
16 | export const mapDispatchToProps = dispatch => ({
17 | fetchProjectChallenges: (projectId) => dispatch(fetchProjectChallenges(projectId)),
18 | })
19 |
20 | const WithProjects =
21 | WrappedComponent => connect(mapStateToProps, mapDispatchToProps)(WrappedComponent)
22 |
23 | export default WithProjects
24 |
--------------------------------------------------------------------------------
/src/components/AdminPane/Manage/TaskAnalysisTable/TaskAnalysisTable.scss:
--------------------------------------------------------------------------------
1 | @import 'theme.scss';
2 |
3 | .task-analysis-table {
4 | @include styled-react-table()
5 |
6 | .status-icon {
7 | height: 20px;
8 | width: auto;
9 | vertical-align: middle;
10 | margin-right: 15px;
11 |
12 | &.created {
13 | fill: $status-created-color;
14 | }
15 |
16 | &.fixed {
17 | fill: $status-fixed-color;
18 | }
19 |
20 | &.falsePositive {
21 | fill: $status-falsePositive-color;
22 | }
23 |
24 | &.skipped {
25 | fill: $status-skipped-color;
26 | }
27 |
28 | &.deleted {
29 | fill: $status-deleted-color;
30 | }
31 |
32 | &.alreadyFixed {
33 | fill: $status-alreadyFixed-color;
34 | }
35 |
36 | &.tooHard {
37 | fill: $status-tooHard-color;
38 | }
39 | }
40 |
41 | .ReactTable .rt-table .rt-td.task-analysis-table__selection-option {
42 | padding: 15px 5px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/SvgSymbol/SvgSymbol.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | /**
5 | * SvgSymbol renders an svg that utilizes a