diff --git a/data/core.yaml b/data/core.yaml index c03cc72da..faf6df7d0 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -808,6 +808,8 @@ en: comment: Please confirm cancel: Cancel submit: Submit + nearbyTask: + title: Jump to nearby task keepRight: tooltip: Data issues detected by keepright.at title: KeepRight Issues diff --git a/data/l10n/core.en.json b/data/l10n/core.en.json index be8810294..5e3d99e36 100644 --- a/data/l10n/core.en.json +++ b/data/l10n/core.en.json @@ -1020,7 +1020,10 @@ "inputPlaceholder": "Change comment (optional)", "comment": "Please confirm", "cancel": "Cancel", - "submit": "Submit" + "submit": "Submit", + "nearbyTask": { + "title": "Jump to nearby task" + } }, "keepRight": { "tooltip": "Data issues detected by keepright.at", diff --git a/modules/services/MapRouletteService.js b/modules/services/MapRouletteService.js index 73745ad18..032b3d951 100644 --- a/modules/services/MapRouletteService.js +++ b/modules/services/MapRouletteService.js @@ -470,6 +470,96 @@ export class MapRouletteService extends AbstractSystem { } + /** + * flyToNearbyTask + * Initiates the process to find and fly to a nearby task based on the current task's challenge ID and task ID. + * @param {Object} task - The current task object containing task details. + */ + flyToNearbyTask(task) { + if (!this.nearbyTaskEnabled) return; + const challengeID = task.parentId; + const taskID = task.id; + if (!challengeID || !taskID) return; + this.filterNearbyTasks(challengeID, taskID); + } + + + /** + * getChallengeDetails + * Retrieves challenge details from cache or API. + * @param {string} challengeID - The ID of the challenge. + * @returns {Promise} Promise resolving with challenge data. + */ + getChallengeDetails(challengeID) { + const cachedChallenge = this._cache.challenges.get(challengeID); + if (cachedChallenge) { + return Promise.resolve(cachedChallenge); + } else { + const challengeUrl = `${MAPROULETTE_API}/challenge/${challengeID}`; + return fetch(challengeUrl) + .then(utilFetchResponse); + } + } + + + /** + * filterNearbyTasks + * Fetches nearby tasks for a given challenge and task ID, and flies to the nearest task. + * @param {string} challengeID - The ID of the challenge. + * @param {string} taskID - The ID of the current task. + * @param {number} [zoom] - Optional zoom level for the map. + */ + filterNearbyTasks(challengeID, taskID, zoom) { + const nearbyTasksUrl = `${MAPROULETTE_API}/challenge/${challengeID}/tasksNearby/${taskID}?excludeSelfLocked=true&limit=1`; + if (!taskID) return; + fetch(nearbyTasksUrl) + .then(utilFetchResponse) + .then(nearbyTasks => { + if (nearbyTasks.length > 0) { + const nearestTaskData = nearbyTasks[0]; + nearestTaskData.parentId = nearestTaskData.parent.toString(); + return this.getChallengeDetails(challengeID) + .then(challengeData => { + // Set the title and parentName using the challenge name + nearestTaskData.title = challengeData.name; + nearestTaskData.parentName = challengeData.name; + + // Create a new QAItem with the updated title and parentName + const nearestTask = new QAItem(this, null, nearestTaskData.id.toString(), nearestTaskData); + const [lng, lat] = nearestTask.location.coordinates; + + const map = this.context.systems.map; + if (map) { + map.centerZoomEase([lng, lat], zoom); + this.selectAndDisplayTask(nearestTask); + } + }); + } + }) + .catch(err => { + console.error('Error fetching nearby tasks for challenge:', challengeID, err); + }); + } + + + /** + * selectAndDisplayTask + * Selects a task and updates the sidebar reflect the selection + * @param {QAItem} task - The task to be selected + */ + selectAndDisplayTask(task) { + const maproulette = this.context.services.maproulette; + if (maproulette) { + if (!(task instanceof QAItem)) return; + + maproulette.currentTask = task; + const selection = new Map(); + selection.set(task.id, task); + this.context.enter('select', { selection }); + } + } + + /** * itemURL * Returns the url to link to task about a challenge diff --git a/modules/ui/maproulette_editor.js b/modules/ui/maproulette_editor.js index 3bd4a9d74..d9a74d235 100644 --- a/modules/ui/maproulette_editor.js +++ b/modules/ui/maproulette_editor.js @@ -374,6 +374,18 @@ export function uiMapRouletteEditor(context) { notAnIssue(d3_event, d, selection); }); + const checkboxSection = buttonEnter.append('div') + .attr('class', 'checkbox-section'); + checkboxSection + .append('input') + .attr('type', 'checkbox') + .attr('id', 'nearbyTaskCheckbox') + .property('checked', maproulette.nearbyTaskEnabled) + .on('change', nearbyTaskChanged); + checkboxSection + .append('label') + .attr('for', 'nearbyTaskCheckbox') + .text(l10n.t('map_data.layers.maproulette.nearbyTask.title')); function isSaveDisabled(d) { return (hasAuth && d.service === 'maproulette') ? null : true; @@ -382,6 +394,16 @@ export function uiMapRouletteEditor(context) { } + function nearbyTaskChanged(d3_event) { + const isChecked = d3_event.target.checked; + const mapRouletteService = context.services.maproulette; + if (mapRouletteService) { + mapRouletteService.nearbyTaskEnabled = isChecked; + // console.log('Nearby Task feature is now', isChecked ? 'enabled' : 'disabled'); + } + } + + function setSaveButtonVisibility(isVisible) { if (isVisible) { d3_select('.note-save').style('display', 'block'); // Show the commentSaveSection @@ -443,7 +465,7 @@ export function uiMapRouletteEditor(context) { buttonSection.select('.submit-button') .text(l10n.t('map_data.layers.maproulette.submit')) .on('click.submit', function(d3_event, d) { - clickSumbit(d3_event, d, selection); + clickSubmit(d3_event, d, selection); }); } @@ -489,7 +511,7 @@ export function uiMapRouletteEditor(context) { selection.call(commentSaveSection); } - function clickSumbit(d3_event, d) { + function clickSubmit(d3_event, d) { this.blur(); // avoid keeping focus on the button - iD#4641 const osm = context.services.osm; const userID = osm._userDetails.id; @@ -505,6 +527,10 @@ export function uiMapRouletteEditor(context) { return; } dispatch.call('change', item); + // Fly to a nearby task if the feature is enabled, after the update + if (maproulette.nearbyTaskEnabled) { + maproulette.flyToNearbyTask(d); + } }); } }