diff --git a/css/65_data.css b/css/65_data.css index 1077607c7..4f1c55cf6 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -53,9 +53,12 @@ border-radius: 20px; } .comment-metadata { - display: flex; + display: -webkit-flex; /* Safari */ + -webkit-flex-wrap: wrap; /* Safari 6.1+ */ flex-flow: row nowrap; + flex-wrap: wrap; justify-content: space-between; + } .comment-author { font-weight: bold; @@ -67,6 +70,9 @@ .comment-text { color: #333; margin-top: 10px; + overflow-y: hidden; /* TODO: give scroll bar or replace with accordion */ + max-height: 250px; + } #new-comment-input { diff --git a/data/core.yaml b/data/core.yaml index b24851df8..3e38f40d4 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -622,7 +622,8 @@ en: newComment: New Comment inputPlaceholder: Enter a comment to share with other users. comment: Comment - commentResolve: Comment & Resolve + commentClose: Comment & Resolve + commentReopen: Comment & Reopen save: Save comment cancel: Cancel help: diff --git a/modules/services/osm.js b/modules/services/osm.js index 92f1d3cf7..e2d0883cc 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -44,6 +44,7 @@ var _tileCache = { loaded: {}, inflight: {}, seen: {} }; var _noteCache = { loaded: {}, inflight: {}, note: {}, rtree: rbush() }; var _userCache = {}; var _changeset = {}; +var _noteChangeset = {}; var _connectionID = 1; var _tileZoom = 16; @@ -309,11 +310,13 @@ export default { _forEach(_tileCache.inflight, abortRequest); _forEach(_noteCache.inflight, abortRequest); if (_changeset.inflight) abortRequest(_changeset.inflight); + if (_noteChangeset.inflight) abortRequest(_changeset.inflight); _tileCache = { loaded: {}, inflight: {}, seen: {} }; _noteCache = { loaded: {}, inflight: {}, note: {}, rtree: rbush() }; _userCache = {}; _changeset = {}; + _noteChangeset = {}; return this; }, @@ -894,5 +897,67 @@ export default { notesCache: function() { return _noteCache; - } + }, + + + toggleNoteStatus: function(note, comment, callback) { + if (!(note instanceof osmNote) && !(this.getNote(note.id))) return; + if (!this.authenticated()) return; + + var that = this; + var cid = _connectionID; + + function done(err, xml) { + if (err) { + // 400 Bad Request, 401 Unauthorized, 403 Forbidden.. + if (err.status === 400 || err.status === 401 || err.status === 403) { + that.logout(); + } + return callback(err); + } + if (that.getConnectionId() !== cid) { + return callback({ message: 'Connection Switched', status: -1 }); + } + + return callback(xml); + } + + var status = note.status === 'open' ? 'close' : 'reopen'; + + var path = '/api/0.6/notes/' + note.id + '/' + status; + path += comment ? '?text=' + comment : ''; + + _noteChangeset.inflight = oauth.xhr({ method: 'POST', path: path }, done); + + }, + + addNoteComment: function(note, comment, callback) { + if (!(note instanceof osmNote) && !(this.getNote(note.id))) return; + if (!this.authenticated()) return; + if (!comment) return; + + var that = this; + var cid = _connectionID; + + function done(err, xml) { + if (err) { + // 400 Bad Request, 401 Unauthorized, 403 Forbidden.. + if (err.status === 400 || err.status === 401 || err.status === 403) { + that.logout(); + } + return callback(err); + } + if (that.getConnectionId() !== cid) { + return callback({ message: 'Connection Switched', status: -1 }); + } + + return callback(xml); + } + + var path = '/api/0.6/notes/' + note.id + '/comment?text=' + comment; + + _noteChangeset.inflight = oauth.xhr({ method: 'POST', path: path }, done); + + }, + }; diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index cb117e769..6038a421a 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -1,5 +1,4 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { uiFormFields } from './form_fields'; import { event as d3_event, @@ -15,14 +14,14 @@ import { } from '../util'; import { utilDetect } from '../util/detect'; - -var _newComment; +import { modeBrowse } from '../modes'; export function uiNoteEditor(context) { var dispatch = d3_dispatch('change', 'cancel', 'save', 'changeInput'); var commentLimit = 600; // add a "more" link to comments longer than this length - var _modified = false; + var _inputValue; + var _newComment; var _note; function localeDateString(s) { @@ -39,6 +38,65 @@ export function uiNoteEditor(context) { render(selection); } + function cancel() { + _newComment = false; + context.selectedNoteID(null); + context.enter(modeBrowse(context)); + } + + function save(updateFunction) { + var osm = context.connection(); + if (!osm) { + context.enter(modeBrowse(context)); + return; + } + + // If user somehow got logged out mid-save, try to reauthenticate.. + // This can happen if they were logged in from before, but the tokens are no longer valid. + if (!osm.authenticated()) { + + // TODO: dispatch 'notAuthenticated' to give warning + + osm.authenticate(function(err) { + if (err) { // quit save mode.. + context.enter(modeBrowse(context)); + return; + } else { + save(updateFunction); // continue where we left off.. + } + }); + return; + } + + function parseResults(results) { + + // call success + + // otherwise, call failure + } + + function success(response) { + console.log('success!', response); + } + + function failure(response) { + console.log('failure!', response); + } + + updateFunction(parseResults); + } + + function toggleNoteStatus(parseResults) { + if (!_note || !_note.status || !context.selectedNoteID) return; + services.osm.toggleNoteStatus(_note, _inputValue, parseResults); + } + + function addNoteComment(parseResults) { + if (!_note || !_note.status || !context.selectedNoteID) return; + services.osm..addNoteComment(_note, _inputValue, parseResults); + } + + function noteHeader(selection) { selection.selectAll('.note-header') @@ -217,16 +275,18 @@ export function uiNoteEditor(context) { .attr('placeholder', t('note.inputPlaceholder')) .attr('maxlength', 1000) .call(utilNoAuto) - .on('input', change(true)) - .on('blur', change()) - .on('change', change()) + .on('input', modified) + .on('blur', modified) + .on('change', modified) .merge(input); - function change(onInput) { - return function() { - dispatch.call('changeInput', this, onInput); - }; + function modified(onInput) { + _modified = !!this.value; + _inputValue = this.value; + + // TODO: fix this event handling & update button text to reflect if there is input + // dispatch.call('inputModified', this, _inputValue); } } @@ -272,20 +332,13 @@ export function uiNoteEditor(context) { .merge(buttonEnter); buttonSection.selectAll('.close-button') - .on('click.close', function() { - console.log('close button clicked'); - }); + .on('click', function() { save(toggleNoteStatus) }); buttonSection.selectAll('.reopen-button') - .on('click.reopen', function() { - console.log('reopen button clicked'); - }); + .on('click', function() { save(toggleNoteStatus) }); buttonSection.selectAll('.cancel-button') - .on('click.cancel', function() { - // var selectedID = commitChanges.entityID(); TODO: cancel note event - // dispatch.call('cancel', this, selectedID); - }); + .on('click.cancel', cancel); buttonSection.selectAll('.save-button') .attr('disabled', function() { @@ -294,7 +347,8 @@ export function uiNoteEditor(context) { }) .on('click.save', function() { this.blur(); // avoid keeping focus on the button - #4641 - // dispatch.call('saveNote', this, _newComment); TODO: saveNote event + save(addNoteComment); + dispatch.call('saveNote', this, _newComment); // TODO: saveNote event }); }