Repository: maproulette/maproulette3
Pull request #2164 (2023-11-12 01:35:55)

Fix task filter layout for smallest TaskBundleWidget size
Task filters and filter controls are too crowded at the smallest allowed widget size. Provided alternate layout at smallest size.

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/maproulette/maproulette3/pull/2164?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette) Report
> Merging [#2164](https://app.codecov.io/gh/maproulette/maproulette3/pull/2164?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette) (0f1fc63) into [main](https://app.codecov.io/gh/maproulette/maproulette3/commit/a0f16cfb6abf06ce3f2acc2f9bec182005f5c600?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette) (a0f16cf) will **increase** coverage by `0.00%`.
> The diff coverage is `0.00%`.

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

```diff
@@           Coverage Diff           @@
##             main    #2164   +/-   ##
=======================================
  Coverage   24.79%   24.80%           
=======================================
  Files         646      646           
  Lines       22162    22164    +2     
  Branches     6808     6810    +2     
=======================================
+ Hits         5496     5497    +1     
- Misses      13944    13945    +1     
  Partials     2722     2722           
```


| [Files](https://app.codecov.io/gh/maproulette/maproulette3/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette) | Coverage Δ | |
|---|---|---|
| [...nents/Widgets/TaskBundleWidget/TaskBundleWidget.js](https://app.codecov.io/gh/maproulette/maproulette3/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette#diff-c3JjL2NvbXBvbmVudHMvV2lkZ2V0cy9UYXNrQnVuZGxlV2lkZ2V0L1Rhc2tCdW5kbGVXaWRnZXQuanM=) | `8.95% <0.00%> (-0.14%)` | :arrow_down: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/maproulette/maproulette3/pull/2164/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=maproulette)

:mega: Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in [Chrome](https://chrome.google.com/webstore/detail/codecov/gedikamndpbemklijjkncpnolildpbgo) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecov/) today!


Patch:
diff --git a/src/components/HOCs/WithKeyboardShortcuts/WithKeyboardShortcuts.js b/src/components/HOCs/WithKeyboardShortcuts/WithKeyboardShortcuts.js
index 84d9d54b..eda85808 100644
--- a/src/components/HOCs/WithKeyboardShortcuts/WithKeyboardShortcuts.js
+++ b/src/components/HOCs/WithKeyboardShortcuts/WithKeyboardShortcuts.js
@@ -61,20 +61,6 @@ const mapDispatchToProps = dispatch => {
       dispatch(resumeKeyboardShortcuts())
     },
     textInputActive: textInputActive,
-    quickKeyHandler: (key, handler, allowModifierKeys=false) => (event => {
-      if (textInputActive(event)) {
-        return // ignore typing in inputs
-      }
-
-      if (!allowModifierKeys && (event.metaKey || event.altKey || event.ctrlKey)) {
-        return
-      }
-
-      if (event.key === key) {
-        handler()
-        event.preventDefault()
-      }
-    }),
   }
 }
 
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/ActiveTaskControls.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/ActiveTaskControls.js
index 705403a6..0f5d73d9 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/ActiveTaskControls.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/ActiveTaskControls.js
@@ -4,12 +4,14 @@ import classNames from 'classnames'
 import { FormattedMessage, injectIntl } from 'react-intl'
 import _get from 'lodash/get'
 import _map from 'lodash/map'
+import _pick from 'lodash/pick'
 import _isEmpty from 'lodash/isEmpty'
 import _remove from 'lodash/remove'
 import _cloneDeep from 'lodash/cloneDeep'
 import _isObject from 'lodash/isObject'
 import _isFinite from 'lodash/isFinite'
 import _isUndefined from 'lodash/isUndefined'
+import { TaskStatus } from '../../../../services/Task/TaskStatus/TaskStatus'
 import { allowedStatusProgressions, isCompletionStatus,
          isFinalStatus, messagesByStatus }
        from '../../../../services/Task/TaskStatus/TaskStatus'
@@ -41,6 +43,8 @@ import { constructChangesetUrl } from '../../../../utils/constructChangesetUrl'
 import { replacePropertyTags } from '../../../../hooks/UsePropertyReplacement/UsePropertyReplacement'
 import './ActiveTaskControls.scss'
 
+const hiddenShortcutGroup = 'taskCompletion'
+const hiddenShortcuts = ['skip', 'falsePositive', 'fixed', 'tooHard', 'alreadyFixed']
 
 /**
  * ActiveTaskControls renders the appropriate controls for the given
@@ -204,6 +208,51 @@ export class ActiveTaskControls extends Component {
                         this.state.requestedNextTask)
   }
 
+  openCompletionModal = (key) => {
+    // Ignore if the shortcut group is not active
+    if (_isEmpty(this.props.activeKeyboardShortcuts?.[hiddenShortcutGroup])) {
+      return;
+    }
+
+    // Handle different keyboard shortcuts
+    switch (key) {
+      case 'f':
+        this.initiateCompletion(TaskStatus.fixed)
+        break
+      case 'd':
+        this.initiateCompletion(TaskStatus.tooHard)
+        break
+      case 'x':
+        this.initiateCompletion(TaskStatus.alreadyFixed)
+        break
+      case 'w':
+        this.initiateCompletion(TaskStatus.skipped)
+        break
+      case 'q':
+        this.initiateCompletion(TaskStatus.falsePositive)
+        break
+      default:
+        break // Handle other keys or do nothing
+    }
+  }
+
+  handleKeyboardShortcuts = (event) => {
+    if (_isEmpty(this.props.activeKeyboardShortcuts[hiddenShortcutGroup])) {
+      return
+    }
+
+    if (this.props.textInputActive(event)) {
+      return // Ignore typing in inputs
+    }
+
+    if (event.metaKey || event.altKey || event.ctrlKey) {
+      return
+    }
+
+    this.openCompletionModal(event.key)
+    event.preventDefault()
+  }
+
   componentDidUpdate(prevProps) {
     if (_get(this.props, 'task.id') !== _get(prevProps, 'task.id')) {
       return this.resetConfirmation()
@@ -232,6 +281,47 @@ export class ActiveTaskControls extends Component {
       return this.setState({revisionLoadBy: TaskReviewLoadMethod.inbox,
                             doneLoadByFromHistory: true})
     }
+
+    if((!AsCooperativeWork(this.props.task).isTagType() || !this.props.user.settings.seeTagFixSuggestions)) {
+      const editMode = this.props.getUserAppSetting ? this.props.getUserAppSetting(this.props.user, 'isEditMode') : false;
+      if (
+        !_isEmpty(this.props.activeKeyboardShortcuts?.[hiddenShortcutGroup]) &&
+        editMode
+      ) {
+        hiddenShortcuts.forEach((shortcut) => {
+          this.props.deactivateKeyboardShortcut(
+            hiddenShortcutGroup,
+            shortcut,
+            this.handleKeyboardShortcuts
+          );
+        });
+      } else if (
+        _isEmpty(this.props.activeKeyboardShortcuts?.[hiddenShortcutGroup]) &&
+        this.props.keyboardShortcutGroups &&
+        this.props.activateKeyboardShortcut &&
+        !editMode
+      ) {
+        hiddenShortcuts.forEach((shortcut) => {
+          this.props.activateKeyboardShortcut(
+            hiddenShortcutGroup,
+            _pick(this.props.keyboardShortcutGroups.taskCompletion, shortcut),
+            this.handleKeyboardShortcuts
+          )
+        })
+      }
+    }
+  }
+
+  componentWillUnmount() {
+    if (!_isEmpty(this.props.activeKeyboardShortcuts?.[hiddenShortcutGroup])) {
+      hiddenShortcuts.forEach((shortcut) => {
+        this.props.deactivateKeyboardShortcut(
+          hiddenShortcutGroup,
+          shortcut,
+          this.handleKeyboardShortcuts
+        )
+      })
+    }
   }
 
   render() {
@@ -337,6 +427,7 @@ export class ActiveTaskControls extends Component {
                 complete={this.initiateCompletion}
                 nextTask={this.next}
                 needsRevised={needsRevised}
+                editMode={editMode}
               />
              }
 
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskAlreadyFixedControl/TaskAlreadyFixedControl.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskAlreadyFixedControl/TaskAlreadyFixedControl.js
index c60b4509..787f7828 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskAlreadyFixedControl/TaskAlreadyFixedControl.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskAlreadyFixedControl/TaskAlreadyFixedControl.js
@@ -1,15 +1,11 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import { FormattedMessage } from 'react-intl'
-import _pick from 'lodash/pick'
-import _isEmpty from 'lodash/isEmpty'
 import { TaskStatus }
        from '../../../../../services/Task/TaskStatus/TaskStatus'
 import Button from '../../../../Button/Button'
 import messages from './Messages'
 
-const shortcutGroup = 'taskCompletion'
-
 /**
  * TaskAlreadyFixedControl displays the a control for marking a task with an
  * already-fixed status.
@@ -17,32 +13,6 @@ const shortcutGroup = 'taskCompletion'
  * @author [Neil Rotstan](https://github.com/nrotstan)
  */
 export default class TaskAlreadyFixedControl extends Component {
-  completeTask = () => {
-    // Ignore if shortcut group is not active
-    if (_isEmpty(this.props.activeKeyboardShortcuts[shortcutGroup])) {
-      return
-    }
-
-    this.props.complete(TaskStatus.alreadyFixed)
-  }
-
-  handleKeyboardShortcuts = this.props.quickKeyHandler(
-    this.props.keyboardShortcutGroups.taskCompletion.alreadyFixed.key,
-    () => this.completeTask()
-  )
-
-  componentDidMount() {
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskCompletion, 'alreadyFixed'),
-      this.handleKeyboardShortcuts)
-  }
-
-  componentWillUnmount() {
-    this.props.deactivateKeyboardShortcut(shortcutGroup, 'alreadyFixed',
-                                          this.handleKeyboardShortcuts)
-  }
-
   render() {
     if (this.props.asLink) {
       return (
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/TaskCompletionStep2.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/TaskCompletionStep2.js
index 81a793ce..90bc5381 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/TaskCompletionStep2.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep2/TaskCompletionStep2.js
@@ -30,7 +30,7 @@ export default class TaskCompletionStep2 extends Component {
     if (this.props.needsRevised) {
       complete = (status) => this.props.complete(status, TaskReviewStatus.needed)
     }
-
+    
     return (
       <div>
         <div className="mr-my-4 mr-grid mr-grid-columns-2 mr-grid-gap-4">
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFalsePositiveControl/TaskFalsePositiveControl.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFalsePositiveControl/TaskFalsePositiveControl.js
index 5183d0e9..da899faa 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFalsePositiveControl/TaskFalsePositiveControl.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFalsePositiveControl/TaskFalsePositiveControl.js
@@ -1,15 +1,11 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import { FormattedMessage } from 'react-intl'
-import _pick from 'lodash/pick'
-import _isEmpty from 'lodash/isEmpty'
 import { TaskStatus }
        from '../../../../../services/Task/TaskStatus/TaskStatus'
 import Button from '../../../../Button/Button'
 import messages from './Messages'
 
-const shortcutGroup = 'taskCompletion'
-
 /**
  * TaskFalsePositiveControl displays a control for marking a task with a
  * false-positive status.
@@ -17,32 +13,6 @@ const shortcutGroup = 'taskCompletion'
  * @author [Neil Rotstan](https://github.com/nrotstan)
  */
 export default class TaskFalsePositiveControl extends Component {
-  completeTask = () => {
-    // Ignore if shortcut group is not active
-    if (_isEmpty(this.props.activeKeyboardShortcuts[shortcutGroup])) {
-      return
-    }
-
-    this.props.complete(TaskStatus.falsePositive)
-  }
-
-  handleKeyboardShortcuts = this.props.quickKeyHandler(
-    this.props.keyboardShortcutGroups.taskCompletion.falsePositive.key,
-    () => this.completeTask()
-  )
-
-  componentDidMount() {
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskCompletion, 'falsePositive'),
-      this.handleKeyboardShortcuts)
-  }
-
-  componentWillUnmount() {
-    this.props.deactivateKeyboardShortcut(shortcutGroup, 'falsePositive',
-                                          this.handleKeyboardShortcuts)
-  }
-
   render() {
     if (this.props.asLink) {
       return (
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFixedControl/TaskFixedControl.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFixedControl/TaskFixedControl.js
index f8595494..e8ad5e75 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFixedControl/TaskFixedControl.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskFixedControl/TaskFixedControl.js
@@ -1,47 +1,17 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import { FormattedMessage } from 'react-intl'
-import _pick from 'lodash/pick'
-import _isEmpty from 'lodash/isEmpty'
 import { TaskStatus }
        from '../../../../../services/Task/TaskStatus/TaskStatus'
 import Button from '../../../../Button/Button'
 import messages from './Messages'
 
-const shortcutGroup = 'taskCompletion'
-
 /**
  * TaskFixedControl displays a control for marking a task with a fixed status.
  *
  * @author [Neil Rotstan](https://github.com/nrotstan)
  */
 export default class TaskFixedControl extends Component {
-  completeTask = () => {
-    // Ignore if shortcut group is not active
-    if (_isEmpty(this.props.activeKeyboardShortcuts[shortcutGroup])) {
-      return
-    }
-
-    this.props.complete(TaskStatus.fixed)
-  }
-
-  handleKeyboardShortcuts = this.props.quickKeyHandler(
-    this.props.keyboardShortcutGroups.taskCompletion.fixed.key,
-    () => this.completeTask()
-  )
-
-  componentDidMount() {
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskCompletion, 'fixed'),
-      this.handleKeyboardShortcuts)
-  }
-
-  componentWillUnmount() {
-    this.props.deactivateKeyboardShortcut(shortcutGroup, 'fixed',
-                                          this.handleKeyboardShortcuts)
-  }
-
   render() {
     if (this.props.asLink) {
       return (
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskSkipControl/TaskSkipControl.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskSkipControl/TaskSkipControl.js
index 75119095..bb3f5ee8 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskSkipControl/TaskSkipControl.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskSkipControl/TaskSkipControl.js
@@ -1,47 +1,17 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import { FormattedMessage } from 'react-intl'
-import _pick from 'lodash/pick'
-import _isEmpty from 'lodash/isEmpty'
 import { TaskStatus }
        from '../../../../../services/Task/TaskStatus/TaskStatus'
 import Button from '../../../../Button/Button'
 import messages from './Messages'
 
-const shortcutGroup = 'taskCompletion'
-
 /**
  * TaskSkipControl displays a control for marking a task with a skipped status.
  *
  * @author [Neil Rotstan](https://github.com/nrotstan)
  */
 export default class TaskSkipControl extends Component {
-  completeTask = () => {
-    // Ignore if shortcut group is not active
-    if (_isEmpty(this.props.activeKeyboardShortcuts[shortcutGroup])) {
-      return
-    }
-
-    this.props.complete(TaskStatus.skipped)
-  }
-
-  handleKeyboardShortcuts = this.props.quickKeyHandler(
-    this.props.keyboardShortcutGroups.taskCompletion.skip.key,
-    () => this.completeTask()
-  )
-
-  componentDidMount() {
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskCompletion, 'skip'),
-      this.handleKeyboardShortcuts)
-  }
-
-  componentWillUnmount() {
-    this.props.deactivateKeyboardShortcut(shortcutGroup, 'skip',
-                                          this.handleKeyboardShortcuts)
-  }
-
   render() {
     if (this.props.asLink) {
       return (
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskTooHardControl/TaskTooHardControl.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskTooHardControl/TaskTooHardControl.js
index 037004a2..a7e53a94 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskTooHardControl/TaskTooHardControl.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskTooHardControl/TaskTooHardControl.js
@@ -1,15 +1,11 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import { FormattedMessage } from 'react-intl'
-import _pick from 'lodash/pick'
-import _isEmpty from 'lodash/isEmpty'
 import { TaskStatus }
        from '../../../../../services/Task/TaskStatus/TaskStatus'
 import Button from '../../../../Button/Button'
 import messages from './Messages'
 
-const shortcutGroup = 'taskCompletion'
-
 /**
  * TaskTooHardControl displays a control for marking a task with a too-hard
  * status.
@@ -17,31 +13,6 @@ const shortcutGroup = 'taskCompletion'
  * @author [Neil Rotstan](https://github.com/nrotstan)
  */
 export default class TaskTooHardControl extends Component {
-  completeTask = () => {
-    // Ignore if shortcut group is not active
-    if (_isEmpty(this.props.activeKeyboardShortcuts[shortcutGroup])) {
-      return
-    }
-
-    this.props.complete(TaskStatus.tooHard)
-  }
-
-  handleKeyboardShortcuts = this.props.quickKeyHandler(
-    this.props.keyboardShortcutGroups.taskCompletion.tooHard.key,
-    () => this.completeTask()
-  )
-
-  componentDidMount() {
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskCompletion, 'tooHard'),
-      this.handleKeyboardShortcuts)
-  }
-
-  componentWillUnmount() {
-    this.props.deactivateKeyboardShortcut(shortcutGroup, 'tooHard',
-                                          this.handleKeyboardShortcuts)
-  }
   render() {
     if (this.props.asLink) {
       return (
diff --git a/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js b/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js
index 30bce7bf..50eb897a 100644
--- a/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js
+++ b/src/components/Widgets/TaskBundleWidget/TaskBundleWidget.js
@@ -77,7 +77,15 @@ const ClusterMap = WithChallengeTaskClusters(
 const shortcutGroup = 'taskEditing'
 
 export default class TaskBundleWidget extends Component {
+  state = {
+    shortcutActive: false,
+  }
+
   bundleTasks = () => {
+    if(_get(this.props, 'taskBundle.tasks.length', 0) > 0){
+      return
+    }
+    
     const selectedArray = Array.from(this.props.selectedTasks.selected.values());
     let bundleTypeMismatch = "";
     
@@ -204,11 +212,6 @@ export default class TaskBundleWidget extends Component {
     if (this.props.task && this.props.selectedTasks && !this.props.isTaskSelected(this.props.task.id)) {
       this.props.selectTasks([this.props.task])
     }
-
-    this.props.activateKeyboardShortcut(
-      shortcutGroup,
-      _pick(this.props.keyboardShortcutGroups.taskEditing, 'completeTogether'),
-      this.handleKeyboardShortcuts)
   }
 
   componentDidUpdate(prevProps) {
@@ -217,6 +220,19 @@ export default class TaskBundleWidget extends Component {
       this.initializeWebsocketSubscription(prevProps)
     }
 
+    if (this.props.selectedTaskCount(this.props.taskInfo.totalCount) > 1 && this.state.shortcutActive === false) {
+      this.setState({ shortcutActive: true })
+      this.props.activateKeyboardShortcut(
+        shortcutGroup,
+        _pick(this.props.keyboardShortcutGroups.taskEditing, 'completeTogether'),
+        this.handleKeyboardShortcuts
+      )
+    } else if (this.state.shortcutActive === true && this.props.selectedTaskCount(this.props.taskInfo.totalCount) <= 1){
+      this.setState({ shortcutActive: false })
+      this.props.deactivateKeyboardShortcut(shortcutGroup, 'completeTogether',
+      this.handleKeyboardShortcuts)
+    }
+
     if (_isFinite(_get(this.props, 'task.id')) &&
         _isFinite(_get(prevProps, 'task.id')) &&
         this.props.task.id !== prevProps.task.id) {
@@ -396,8 +412,9 @@ const BuildBundle = props => {
           <MapPane showLasso>{map}</MapPane>
         }
       </div>
-      <div className="mr-my-4 mr-px-4 xl:mr-flex xl:mr-justify-between mr-items-center">
-        <div className='mr-flex mr-items-center'>
+
+      <div className={props.widgetLayout && props.widgetLayout?.w === 4 ? "mr-my-4 mr-px-4 mr-space-y-3" : "mr-my-4 mr-px-4 xl:mr-flex xl:mr-justify-between mr-items-center"}>
+        <div className="mr-flex mr-items-center">
           <p className="mr-text-base mr-uppercase mr-text-mango mr-mr-8">
             <FormattedMessage {...messages.filterListLabel} />
           </p>
@@ -414,10 +431,10 @@ const BuildBundle = props => {
           </ul>
         </div>
         
-        <div className='mr-flex mr-space-x-3 mr-items-center mr-justify-end'>
+        <div className={`mr-flex mr-space-x-3 mr-items-center ${props.widgetLayout && props.widgetLayout?.w === 4 ? 'mr-justify-between' : 'mr-justify-end'}`}>
         {<ClearFiltersControl clearFilters={props.clearAllFilters}/>}
           <Dropdown
-          className='mr-flex mr-items-center'
+          className="mr-flex mr-items-center"
             dropdownButton={(dropdown) => (
               <button
                 onClick={dropdown.toggleDropdownVisible}
diff --git a/src/components/Widgets/TaskMapWidget/RapidEditor/EditSwitch.js b/src/components/Widgets/TaskMapWidget/RapidEditor/EditSwitch.js
index 4816fde3..5147a959 100644
--- a/src/components/Widgets/TaskMapWidget/RapidEditor/EditSwitch.js
+++ b/src/components/Widgets/TaskMapWidget/RapidEditor/EditSwitch.js
@@ -21,8 +21,8 @@ export class EditSwitch extends Component {
     return (
       <div className="mr-flex mr-justify-center">
         <label className="switch-container">
-          <input type="checkbox" checked={editModeOn} onChange={() => null}/>
-          <span className="slider round" onClick={this.toggleVisible}></span>
+          <input type="checkbox" checked={editModeOn} />
+          <span className="slider round" onClick={() => this.toggleVisible()}></span>
         </label>
         <span className="mr-ml-2">
           {editModeOn ? 'Edit Mode' : 'Classic Mode'}
diff --git a/src/services/Error/Messages.js b/src/services/Error/Messages.js
index dc392bf9..4f24badf 100644
--- a/src/services/Error/Messages.js
+++ b/src/services/Error/Messages.js
@@ -173,7 +173,7 @@ export default defineMessages({
   },
   challengeSaveDetailsFailure: {
     id: "Errors.challengeSaveFailure.challengeSaveDetailsFailure",
-    defaultMessage: "Unable to save your changes. It is likely a duplicate challenge name.",
+    defaultMessage: "Unable to save your changes.",
   },
   challengeSaveNameFailure: {
     id: "Errors.challengeSaveFailure.challengeSaveNameFailure",
diff --git a/src/services/KeyboardShortcuts/KeyMappings.js b/src/services/KeyboardShortcuts/KeyMappings.js
index 0b54c7b8..46a5098f 100644
--- a/src/services/KeyboardShortcuts/KeyMappings.js
+++ b/src/services/KeyboardShortcuts/KeyMappings.js
@@ -24,7 +24,7 @@ export default {
   taskEditing: {
     cancel: {key: 'Escape', label: messages.cancel, keyLabel: messages.escapeLabel},
     fitBounds: {key: '0', label: messages.fitBounds},
-    completeTogether: {key: 'c', label: messages.completeTogether}
+    completeTogether: {key: 'b', label: messages.completeTogether}
   },
   layers: {
     layerOSMData: {key: 'o', label: messages.layerOSMData},


Test patch:
diff --git a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep1/TaskCompletionStep1.test.js b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep1/TaskCompletionStep1.test.js
index 9193048d..d9689011 100644
--- a/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep1/TaskCompletionStep1.test.js
+++ b/src/components/TaskPane/ActiveTaskDetails/ActiveTaskControls/TaskCompletionStep1/TaskCompletionStep1.test.js
@@ -53,7 +53,6 @@ describe("TaskCompletionStep1", () => {
         keyboardShortcutGroups={{ taskCompletion: { fixed: { key: 123 } } }}
         activateKeyboardShortcutGroup={() => null}
         deactivateKeyboardShortcutGroup={() => null}
-        quickKeyHandler={() => null}
         activateKeyboardShortcut={() => null}
         deactivateKeyboardShortcut={() => null}
         needsRevised
@@ -78,7 +77,6 @@ describe("TaskCompletionStep1", () => {
         keyboardShortcutGroups={{ taskCompletion: { fixed: { key: 123 } } }}
         activateKeyboardShortcutGroup={() => null}
         deactivateKeyboardShortcutGroup={() => null}
-        quickKeyHandler={() => null}
         activateKeyboardShortcut={() => null}
         deactivateKeyboardShortcut={() => null}
         needsRevised
@@ -112,7 +110,6 @@ describe("TaskCompletionStep1", () => {
         keyboardShortcutGroups={{ taskCompletion: { falsePositive: { key: 123 } } }}
         activateKeyboardShortcutGroup={() => null}
         deactivateKeyboardShortcutGroup={() => null}
-        quickKeyHandler={() => null}
         activateKeyboardShortcut={() => null}
         deactivateKeyboardShortcut={() => null}
         needsRevised
@@ -146,7 +143,6 @@ describe("TaskCompletionStep1", () => {
         keyboardShortcutGroups={{ taskCompletion: { tooHard: { key: 123 } } }}
         activateKeyboardShortcutGroup={() => null}
         deactivateKeyboardShortcutGroup={() => null}
-        quickKeyHandler={() => null}
         activateKeyboardShortcut={() => null}
         deactivateKeyboardShortcut={() => null}
         needsRevised
@@ -180,7 +176,6 @@ describe("TaskCompletionStep1", () => {
         keyboardShortcutGroups={{ taskCompletion: { alreadyFixed: { key: 123 } } }}
         activateKeyboardShortcutGroup={() => null}
         deactivateKeyboardShortcutGroup={() => null}
-        quickKeyHandler={() => null}
         activateKeyboardShortcut={() => null}
         deactivateKeyboardShortcut={() => null}
         needsRevised