Skip to content

Commit

Permalink
automatically download adjacent ways when splitting a line
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yle committed Jan 3, 2025
1 parent 28183c7 commit 42bada7
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 4 deletions.
6 changes: 6 additions & 0 deletions modules/behavior/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export function behaviorOperation(context) {
var disabled = _operation.disabled();

if (disabled) {
const interrupt = _operation.interrupts?.[disabled];
if (interrupt) {
interrupt();
return;
}

context.ui().flash
.duration(4000)
.iconName('#iD-operation-' + _operation.id)
Expand Down
56 changes: 55 additions & 1 deletion modules/operations/split.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { t } from '../core/localizer';
import { actionSplit } from '../actions/split';
import { behaviorOperation } from '../behavior/operation';
import { modeSelect } from '../modes/select';
import { uiAsyncModal } from '../ui/modal_async';
import { uiLoading } from '../ui';


export function operationSplit(context, selectedIDs) {
Expand Down Expand Up @@ -65,10 +67,62 @@ export function operationSplit(context, selectedIDs) {
return false;
};

operation.interrupts = {
async parent_incomplete() {
const graph = context.graph();

const confirmed = await uiAsyncModal(context).open(
t.append('operations.split.title'),
t.append('operations.split.parent_incomplete'),
);

if (!confirmed) return; // user cancelled the operation


const loading = uiLoading(context).blocking(true);
context.container().call(loading);

/** @type {Set<string>} */
const requiredWayIds = new Set();

// find vertex->ways->relations, then find the adjacent way for
// each relation.
for (const nodeId of _vertexIds) {
const ways = _action.waysForNode(nodeId, graph);
for (const way of ways) {
const relations = graph.parentRelations(way);
for (const relation of relations) {
const indexOfWay = relation.members.findIndex(m => m.id === way.id);

const prevWay = relation.members[indexOfWay - 1]?.id;
const nextWay = relation.members[indexOfWay + 1]?.id;

if (prevWay) requiredWayIds.add(prevWay);
if (nextWay) requiredWayIds.add(nextWay);
}
}
}

// only download the ways that aren't downloaded yet
const promises = [...requiredWayIds]
.filter(wayId => !context.graph().hasEntity(wayId))
.map((wayId) => new Promise(resolve => {
context.loadEntity(wayId, resolve);
}));

await Promise.all(promises);

loading.close();

// now we can resume the interrupted operation
operation();
}
};


operation.tooltip = function() {
var disable = operation.disabled();
return disable ?
return disable && !operation.interrupts?.[disable] ?
t.append('operations.split.' + disable) :
t.append('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
};
Expand Down
15 changes: 12 additions & 3 deletions modules/ui/edit_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ export function uiEditMenu(context) {
// update
buttonsEnter
.merge(buttons)
.classed('disabled', function(d) { return d.disabled(); });
.classed('disabled', d => {
// if a disabled operation is interruptable, then don't

Check failure on line 133 in modules/ui/edit_menu.js

View workflow job for this annotation

GitHub Actions / Check for spelling errors

interruptable ==> interruptible

Check failure on line 133 in modules/ui/edit_menu.js

View workflow job for this annotation

GitHub Actions / Check for spelling errors

interruptable ==> interruptible
// show it as disabled.
const reason = d.disabled();
return reason && !d.interrupts?.[reason];
});

updatePosition();

Expand Down Expand Up @@ -157,8 +162,12 @@ export function uiEditMenu(context) {
utilHighlightEntities(operation.relatedEntityIds(), false, context);
}

if (operation.disabled()) {
if (lastPointerUpType === 'touch' ||
const disabled = operation.disabled();
if (disabled) {
const interrupt = operation.interrupts?.[disabled];
if (interrupt) {
interrupt();
} else if (lastPointerUpType === 'touch' ||
lastPointerUpType === 'pen') {
// there are no tooltips for touch interactions so flash feedback instead
context.ui().flash
Expand Down
55 changes: 55 additions & 0 deletions modules/ui/modal_async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { t } from '../core/localizer';
import { uiConfirm } from './confirm';

/** @param {iD.Context} context */
export function uiAsyncModal(context) {
let _modal;

/**
* Open a model, and returns a promise. The promise
* resolves with `true` if the user clicked 'Okay',
* or `false` if they clicked 'Cancel'
* @returns {Promise<boolean>}
*/
function open(title, subtitle) {
return new Promise((resolve) => {
context.container().call(selection => {
_modal = uiConfirm(selection).okButton();

_modal.select('.modal-section.header')
.append('h3')
.call(title);

// insert the modal body
const textSection = _modal.select('.modal-section.message-text');
textSection.call(subtitle);

// insert a cancel button
const buttonSection = _modal.select('.modal-section.buttons');

buttonSection
.insert('button', '.ok-button')
.attr('class', 'button cancel-button secondary-action')
.call(t.append('confirm.cancel'));


buttonSection.select('.cancel-button')
.on('click.cancel', () => {
_modal.remove();
resolve(false);
});

buttonSection.select('.ok-button')
.on('click.save', () => resolve(true));
});
});
}

function close() {
_modal.remove();
_modal = undefined;
}


return { open, close };
}

0 comments on commit 42bada7

Please sign in to comment.