Files
iD/modules/modes/save.js
Quincy Morgan a1af118f0e Ensure locales and presets are loaded before the UI loads (close #7406)
Consolidate localization behavior and init to a coreLocalizer function and singleton
Explicitly support `en-US` locale
Rename coreData to coreFileFetcher and export a singleton rather than using a property of coreContext
Add `apiConnections` property of coreContext to simplify adding a source switcher
Replace some init functions with re-callable, promise-supporting `ensureLoaded` functions
Make coreContext itself load the UI if a container has been specified at init time
Fix code tests
2020-03-31 12:23:31 -07:00

249 lines
6.8 KiB
JavaScript

import { event as d3_event, select as d3_select } from 'd3-selection';
import { t } from '../core/localizer';
import { modeBrowse } from './browse';
import { services } from '../services';
import { uiConflicts } from '../ui/conflicts';
import { uiConfirm } from '../ui/confirm';
import { uiCommit } from '../ui/commit';
import { uiSuccess } from '../ui/success';
import { utilKeybinding } from '../util';
export function modeSave(context) {
var mode = { id: 'save' };
var keybinding = utilKeybinding('modeSave');
var commit = uiCommit(context)
.on('cancel', cancel);
var _location;
var _success;
var uploader = context.uploader()
.on('saveStarted.modeSave', function() {
keybindingOff();
})
// fire off some async work that we want to be ready later
.on('willAttemptUpload.modeSave', prepareForSuccess)
.on('progressChanged.modeSave', showProgress)
.on('resultNoChanges.modeSave', function() {
cancel();
})
.on('resultErrors.modeSave', showErrors)
.on('resultConflicts.modeSave', showConflicts)
.on('resultSuccess.modeSave', showSuccess);
function cancel() {
context.enter(modeBrowse(context));
}
function showProgress(num, total) {
var modal = context.container().select('.loading-modal .modal-section');
var progress = modal.selectAll('.progress')
.data([0]);
// enter/update
progress.enter()
.append('div')
.attr('class', 'progress')
.merge(progress)
.text(t('save.conflict_progress', { num: num, total: total }));
}
function showConflicts(changeset, conflicts, origChanges) {
var selection = context.container()
.select('.sidebar')
.append('div')
.attr('class','sidebar-component');
context.container().selectAll('.main-content')
.classed('active', true)
.classed('inactive', false);
var ui = uiConflicts(context)
.conflictList(conflicts)
.origChanges(origChanges)
.on('cancel', function() {
context.container().selectAll('.main-content')
.classed('active', false)
.classed('inactive', true);
selection.remove();
keybindingOn();
uploader.cancelConflictResolution();
})
.on('save', function() {
context.container().selectAll('.main-content')
.classed('active', false)
.classed('inactive', true);
selection.remove();
uploader.processResolvedConflicts(changeset);
});
selection.call(ui);
}
function showErrors(errors) {
keybindingOn();
var selection = uiConfirm(context.container());
selection
.select('.modal-section.header')
.append('h3')
.text(t('save.error'));
addErrors(selection, errors);
selection.okButton();
}
function addErrors(selection, data) {
var message = selection
.select('.modal-section.message-text');
var items = message
.selectAll('.error-container')
.data(data);
var enter = items.enter()
.append('div')
.attr('class', 'error-container');
enter
.append('a')
.attr('class', 'error-description')
.attr('href', '#')
.classed('hide-toggle', true)
.text(function(d) { return d.msg || t('save.unknown_error_details'); })
.on('click', function() {
d3_event.preventDefault();
var error = d3_select(this);
var detail = d3_select(this.nextElementSibling);
var exp = error.classed('expanded');
detail.style('display', exp ? 'none' : 'block');
error.classed('expanded', !exp);
});
var details = enter
.append('div')
.attr('class', 'error-detail-container')
.style('display', 'none');
details
.append('ul')
.attr('class', 'error-detail-list')
.selectAll('li')
.data(function(d) { return d.details || []; })
.enter()
.append('li')
.attr('class', 'error-detail-item')
.text(function(d) { return d; });
items.exit()
.remove();
}
function showSuccess(changeset) {
commit.reset();
var ui = _success
.changeset(changeset)
.location(_location)
.on('cancel', function() { context.ui().sidebar.hide(); });
context.enter(modeBrowse(context).sidebar(ui));
}
function keybindingOn() {
d3_select(document)
.call(keybinding.on('⎋', cancel, true));
}
function keybindingOff() {
d3_select(document)
.call(keybinding.unbind);
}
// Reverse geocode current map location so we can display a message on
// the success screen like "Thank you for editing around place, region."
function prepareForSuccess() {
_success = uiSuccess(context);
_location = null;
if (!services.geocoder) return;
services.geocoder.reverse(context.map().center(), function(err, result) {
if (err || !result || !result.address) return;
var addr = result.address;
var place = (addr && (addr.town || addr.city || addr.county)) || '';
var region = (addr && (addr.state || addr.country)) || '';
var separator = (place && region) ? t('success.thank_you_where.separator') : '';
_location = t('success.thank_you_where.format',
{ place: place, separator: separator, region: region }
);
});
}
mode.enter = function() {
// Show sidebar
context.ui().sidebar.expand();
function done() {
context.ui().sidebar.show(commit);
}
keybindingOn();
context.container().selectAll('.main-content')
.classed('active', false)
.classed('inactive', true);
var osm = context.connection();
if (!osm) {
cancel();
return;
}
if (osm.authenticated()) {
done();
} else {
osm.authenticate(function(err) {
if (err) {
cancel();
} else {
done();
}
});
}
};
mode.exit = function() {
keybindingOff();
context.container().selectAll('.main-content')
.classed('active', true)
.classed('inactive', false);
context.ui().sidebar.hide();
};
return mode;
}