From 87f001474f41730e807730e1cbb8cf712cece964 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:05:49 +0800 Subject: [PATCH 01/18] Add set button and view button at mapillary field (#9339) more info see https://github.com/openstreetmap/iD/issues/6196#issuecomment-751460516 I do the b. (set button) and c. (view button) order: click OSM object -> click mapillary layer will close the OSM input field. need to do in reverse order --- data/core.yaml | 2 ++ modules/ui/fields/input.js | 60 +++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/data/core.yaml b/data/core.yaml index 864efd53d7..b07669557e 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -786,6 +786,8 @@ en: inch: in max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." + set_mapillary: "Sets the current ID of mapillary" + show_mapillary_from_field: "Show image on viewer" background: title: Background description: Background Settings diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index c5061b5ea0..107e9e10f2 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -13,6 +13,7 @@ import { isColourValid } from '../../osm/tags'; import { uiLengthIndicator } from '..'; import { uiTooltip } from '../tooltip'; import { isEqual } from 'lodash-es'; +import { services } from '../../services'; export { uiFieldText as uiFieldColour, @@ -30,6 +31,7 @@ export function uiFieldText(field, context) { var dispatch = d3_dispatch('change'); var input = d3_select(null); var outlinkButton = d3_select(null); + var mapillaryViewButton = d3_select(null); var wrap = d3_select(null); var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue()); var _entityIDs = []; @@ -169,8 +171,60 @@ export function uiFieldText(field, context) { change()(); }); } else if (field.type === 'identifier' && field.urlFormat && field.pattern) { - input.attr('type', 'text'); + + if (field.id==='mapillary'){ + const service = services.mapillary; + // set button + wrap.selectAll('.mapillary-set-current') + .data([0]) + .enter() + .append('button') + .attr('class', 'form-field-button mapillary-set-current') + .call(svgIcon('#fas-rotate')) + .call(uiTooltip().title(() => t.append('inspector.set_mapillary'))) + .on('click', function(d3_event) { + d3_event.preventDefault(); + const image = service.getActiveImage(); + if (!image) return; + service + .ensureViewerLoaded(context) + .then(function() { + service + .showViewer(context) + .selectImage(context, image.id) + .initViewer(context); + utilGetSetValue(input, image.id); + change()(); + }); + }) + .merge(wrap.selectAll('.mapillary-set-current')) + .classed('disabled', () => !service.getActiveImage()); + // view button + mapillaryViewButton = wrap.selectAll('.mapillary-show-view') + .data([0]) + .enter() + .append('button') + .attr('class', 'form-field-button mapillary-show-view') + .call(svgIcon('#fas-eye')) + .call(uiTooltip().title(() => t.append('inspector.show_mapillary_from_field'))) + .on('click', function(d3_event) { + d3_event.preventDefault(); + if ( !utilGetSetValue(input).trim()) return; + service + .ensureViewerLoaded(context) + .then(function() { + service + .showViewer(context) + .selectImage(context, utilGetSetValue(input).trim()) + .initViewer(context); + }); + + }) + .merge(wrap.selectAll('.mapillary-show-view')) + .classed('disabled', () => !utilGetSetValue(input).trim()); + } + outlinkButton = wrap.selectAll('.foreign-id-permalink') .data([0]); @@ -525,6 +579,10 @@ export function uiFieldText(field, context) { outlinkButton.classed('disabled', disabled); } + if (mapillaryViewButton && !mapillaryViewButton.empty()) { + mapillaryViewButton.classed('disabled', !utilGetSetValue(input).trim()); + } + if (!isMixed) { _lengthIndicator.update(tags[field.key]); } From a2296cf03fb43cdbd6686742dd794ee61daabd54 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:20:12 +0800 Subject: [PATCH 02/18] fix setButton when imageChange disabled --- modules/ui/fields/input.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 107e9e10f2..0b0c4af1e3 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -32,6 +32,7 @@ export function uiFieldText(field, context) { var input = d3_select(null); var outlinkButton = d3_select(null); var mapillaryViewButton = d3_select(null); + var mapillarySetButton = d3_select(null); var wrap = d3_select(null); var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue()); var _entityIDs = []; @@ -176,7 +177,7 @@ export function uiFieldText(field, context) { if (field.id==='mapillary'){ const service = services.mapillary; // set button - wrap.selectAll('.mapillary-set-current') + mapillarySetButton = wrap.selectAll('.mapillary-set-current') .data([0]) .enter() .append('button') @@ -187,6 +188,7 @@ export function uiFieldText(field, context) { d3_event.preventDefault(); const image = service.getActiveImage(); if (!image) return; + if (image.id===utilGetSetValue(input).trim()) return; service .ensureViewerLoaded(context) .then(function() { @@ -199,7 +201,19 @@ export function uiFieldText(field, context) { }); }) .merge(wrap.selectAll('.mapillary-set-current')) - .classed('disabled', () => !service.getActiveImage()); + .classed('disabled', () => { + const image = service.getActiveImage(); + if (!image) return true; + if (image.id===utilGetSetValue(input).trim()) return true; + }); + service.on('imageChanged', function() { + mapillarySetButton.classed('disabled', () => { + const image = service.getActiveImage(); + if (!image) return true; + if (image.id===utilGetSetValue(input).trim()) return true; + return false; + }); + }); // view button mapillaryViewButton = wrap.selectAll('.mapillary-show-view') .data([0]) @@ -219,7 +233,6 @@ export function uiFieldText(field, context) { .selectImage(context, utilGetSetValue(input).trim()) .initViewer(context); }); - }) .merge(wrap.selectAll('.mapillary-show-view')) .classed('disabled', () => !utilGetSetValue(input).trim()); @@ -583,6 +596,19 @@ export function uiFieldText(field, context) { mapillaryViewButton.classed('disabled', !utilGetSetValue(input).trim()); } + if (mapillarySetButton && !mapillarySetButton.empty()) { + const service = services.mapillary; + mapillarySetButton.classed('disabled', () => { + const image = service.getActiveImage(); + if (!image) return true; + if (image.id===utilGetSetValue(input).trim()) { + return true; + } else { + return false; + } + }); + } + if (!isMixed) { _lengthIndicator.update(tags[field.key]); } From 33f4f8ee5c458016a4c9c4acf44b967549f167dc Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:49:47 +0800 Subject: [PATCH 03/18] fix button title --- modules/ui/fields/input.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 0b0c4af1e3..75989e0dda 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -183,7 +183,7 @@ export function uiFieldText(field, context) { .append('button') .attr('class', 'form-field-button mapillary-set-current') .call(svgIcon('#fas-rotate')) - .call(uiTooltip().title(() => t.append('inspector.set_mapillary'))) + .attr('title', t('inspector.set_mapillary')) .on('click', function(d3_event) { d3_event.preventDefault(); const image = service.getActiveImage(); @@ -221,7 +221,7 @@ export function uiFieldText(field, context) { .append('button') .attr('class', 'form-field-button mapillary-show-view') .call(svgIcon('#fas-eye')) - .call(uiTooltip().title(() => t.append('inspector.show_mapillary_from_field'))) + .attr('title', t('inspector.show_mapillary_from_field')) .on('click', function(d3_event) { d3_event.preventDefault(); if ( !utilGetSetValue(input).trim()) return; From 5890b79472882e37be0a9dd833cf6c08737e24b3 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:34:14 +0800 Subject: [PATCH 04/18] Add set mapillary id button in the mapillary photo viewer It is the a. part in https://github.com/openstreetmap/iD/issues/6196#issuecomment-751460516 still need to add button disabled logic --- css/60_photos.css | 9 ++++++ modules/ui/photoviewer.js | 60 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/css/60_photos.css b/css/60_photos.css index 729f2ba6f2..6cec267d6f 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -33,6 +33,15 @@ z-index: 50; } +.photoviewer button.set-mapillary-image-ID-field { + border-radius: 0; + padding: 5px; + position: absolute; + left: 5px; + top: 5px; + z-index: 50; +} + .photoviewer button.resize-handle-xy { border-radius: 0; position: absolute; diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 0d1240ff88..6c1f876359 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -6,7 +6,7 @@ import { t } from '../core/localizer'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { svgIcon } from '../svg/icon'; import { utilGetDimensions } from '../util/dimensions'; -import { utilRebind } from '../util'; +import { utilRebind , utilGetSetValue } from '../util'; import { services } from '../services'; export function uiPhotoviewer(context) { @@ -61,6 +61,64 @@ export function uiPhotoviewer(context) { buildResizeListener(selection, 'resize', dispatch, { resizeOnY: true }) ); + if (services.mapillary) { + addMapillayIdButton(); + } + + // add set-mapillary-button in mapillary viewer + function addMapillayIdButton () { + + const service = services.mapillary; + selection + .append('button') + .attr('class', 'set-mapillary-image-ID-field') + .on('click', function(d3_event) { + d3_event.preventDefault(); + const editorPane = d3_select('.entity-editor-pane'); + const inspector_wrap = d3_select('.inspector-wrap'); + const mailallary_image_ID_field = d3_select('.wrap-form-field-mapillary'); + const changeEvent = new Event('change'); + if (!editorPane) return; + + if (!editorPane.classed('hide')&&!inspector_wrap.classed('inspector-hidden')){ + // check if mapillary image id field exist + if (!mailallary_image_ID_field.empty()) { + // insert id + const image = service.getActiveImage(); + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + utilGetSetValue(fieldInput,image.id); + + // trigger change at field + fieldInput.node().focus(); + fieldInput.node().dispatchEvent(changeEvent); + } else { + // open the Mapillary field + const comboboxInput = d3_select('.value.combobox-input'); + const EnterEvent = new KeyboardEvent('keydown',{keyCode : 13}); + utilGetSetValue(comboboxInput,'Mapillary Image ID'); + comboboxInput.node().focus(); + comboboxInput.node().dispatchEvent(EnterEvent); + + // insert id + const image = service.getActiveImage(); + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + utilGetSetValue(fieldInput,image.id); + + // trigger change at field + fieldInput.node().focus(); + fieldInput.node().dispatchEvent(changeEvent); + } + } else { + /* eslint-disable no-console */ + console.log('editorPane hide'); + /* eslint-disable no-console */ + } + }) + .append('div') + .call(svgIcon('#fas-rotate')) + .attr('title', t('inspector.set_mapillary')); + } + function buildResizeListener(target, eventName, dispatch, options) { var resizeOnX = !!options.resizeOnX; From 6523ae6f9428a231bb497b56fe7d0f201581c4c7 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:20:33 +0800 Subject: [PATCH 05/18] Update icon to '#iD-operation-merge' --- modules/ui/photoviewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 6c1f876359..a3b3e3bce0 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -115,7 +115,7 @@ export function uiPhotoviewer(context) { } }) .append('div') - .call(svgIcon('#fas-rotate')) + .call(svgIcon('#iD-operation-merge')) .attr('title', t('inspector.set_mapillary')); } From 2b0e0f6349f0a9d89746b23eeb83aff4d1deb6fe Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:36:05 +0800 Subject: [PATCH 06/18] update core.yaml --- data/core.yaml | 5 +++-- modules/ui/fields/input.js | 4 ++-- modules/ui/photoviewer.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index b07669557e..9db7df56a6 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -786,8 +786,9 @@ en: inch: in max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." - set_mapillary: "Sets the current ID of mapillary" - show_mapillary_from_field: "Show image on viewer" + set_photo_from_viewer: "Store this photo on the currently selected map object" + set_photo_from_field: "Use the currently displayed photo" + show_photo_from_field: "Open image in viewer" background: title: Background description: Background Settings diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 75989e0dda..fdef4c9ff5 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -183,7 +183,7 @@ export function uiFieldText(field, context) { .append('button') .attr('class', 'form-field-button mapillary-set-current') .call(svgIcon('#fas-rotate')) - .attr('title', t('inspector.set_mapillary')) + .attr('title', t('inspector.set_photo_from_field')) .on('click', function(d3_event) { d3_event.preventDefault(); const image = service.getActiveImage(); @@ -221,7 +221,7 @@ export function uiFieldText(field, context) { .append('button') .attr('class', 'form-field-button mapillary-show-view') .call(svgIcon('#fas-eye')) - .attr('title', t('inspector.show_mapillary_from_field')) + .attr('title', t('inspector.show_photo_from_field')) .on('click', function(d3_event) { d3_event.preventDefault(); if ( !utilGetSetValue(input).trim()) return; diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index a3b3e3bce0..6eeb0cc2b6 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -116,7 +116,7 @@ export function uiPhotoviewer(context) { }) .append('div') .call(svgIcon('#iD-operation-merge')) - .attr('title', t('inspector.set_mapillary')); + .attr('title', t('inspector.set_photo_from_viewer')); } function buildResizeListener(target, eventName, dispatch, options) { From 0ef947d4257993a1e5ad7b787fb8924927a8da7a Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:07:49 +0800 Subject: [PATCH 07/18] Update set_photo_from_viewer button create/remove logic refactor the code expandability for other photo_overlay Not sure about context.features().on('change'), and hash part --- css/60_photos.css | 2 +- modules/ui/photoviewer.js | 132 ++++++++++++++++++++++---------------- 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index 6cec267d6f..1368ad40f9 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -33,7 +33,7 @@ z-index: 50; } -.photoviewer button.set-mapillary-image-ID-field { +.photoviewer button.set_photo_from_viewer { border-radius: 0; padding: 5px; position: absolute; diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 6eeb0cc2b6..d72baa4b66 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -6,7 +6,7 @@ import { t } from '../core/localizer'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { svgIcon } from '../svg/icon'; import { utilGetDimensions } from '../util/dimensions'; -import { utilRebind , utilGetSetValue } from '../util'; +import { utilRebind, utilGetSetValue, utilStringQs } from '../util'; import { services } from '../services'; export function uiPhotoviewer(context) { @@ -61,62 +61,86 @@ export function uiPhotoviewer(context) { buildResizeListener(selection, 'resize', dispatch, { resizeOnY: true }) ); - if (services.mapillary) { - addMapillayIdButton(); - } + // set_photo_from_viewer button + context.features() + // need fix : click sidebar not listen, maybe has more precise way to listen to + .on('change', function() { + const hash = utilStringQs(window.location.hash); + let [serviceId, photoId] = []; + let photo_overlay = []; + if (hash.photo) { + [serviceId, photoId] = hash.photo.split('/'); + } - // add set-mapillary-button in mapillary viewer - function addMapillayIdButton () { + if (hash.photo_overlay) { + photo_overlay = hash.photo_overlay.split(','); + } + // check photoviewer open && photo and layer match && currently only support mapillary + if (serviceId && photo_overlay.includes(serviceId) && serviceId === 'mapillary') { + context.container() + .on('click.set_photo_from_viewer', function() { + const inspector_wrap = d3_select('.inspector-wrap'); + const editorPane = d3_select('.entity-editor-pane'); + + const button = selection.selectAll('.set_photo_from_viewer').data([0]); + + if (!inspector_wrap.classed('inspector-hidden') && + !editorPane.classed('hide') && + context.mode().id === 'select') { + button.enter() + .append('button') + .attr('class', 'set_photo_from_viewer') + .on('click', function() { + if (serviceId === 'mapillary' && services.mapillary) { set_photo_from_mapillary_viewer(); } + }) + .append('div') + .call(svgIcon('#iD-operation-merge')) + .attr('title', t('inspector.set_photo_from_viewer')); + } else { + button.remove(); + } + }); + } else { + // remove listener and button + context.container() + .on('click.set_photo_from_viewer', null); + const button = selection.selectAll('.set_photo_from_viewer'); + button.remove(); + } + }); + function set_photo_from_mapillary_viewer() { + const mapillary_image_ID_field = d3_select('.wrap-form-field-mapillary'); + const changeEvent = new Event('change'); const service = services.mapillary; - selection - .append('button') - .attr('class', 'set-mapillary-image-ID-field') - .on('click', function(d3_event) { - d3_event.preventDefault(); - const editorPane = d3_select('.entity-editor-pane'); - const inspector_wrap = d3_select('.inspector-wrap'); - const mailallary_image_ID_field = d3_select('.wrap-form-field-mapillary'); - const changeEvent = new Event('change'); - if (!editorPane) return; - - if (!editorPane.classed('hide')&&!inspector_wrap.classed('inspector-hidden')){ - // check if mapillary image id field exist - if (!mailallary_image_ID_field.empty()) { - // insert id - const image = service.getActiveImage(); - const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - utilGetSetValue(fieldInput,image.id); - - // trigger change at field - fieldInput.node().focus(); - fieldInput.node().dispatchEvent(changeEvent); - } else { - // open the Mapillary field - const comboboxInput = d3_select('.value.combobox-input'); - const EnterEvent = new KeyboardEvent('keydown',{keyCode : 13}); - utilGetSetValue(comboboxInput,'Mapillary Image ID'); - comboboxInput.node().focus(); - comboboxInput.node().dispatchEvent(EnterEvent); - - // insert id - const image = service.getActiveImage(); - const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - utilGetSetValue(fieldInput,image.id); - - // trigger change at field - fieldInput.node().focus(); - fieldInput.node().dispatchEvent(changeEvent); - } - } else { - /* eslint-disable no-console */ - console.log('editorPane hide'); - /* eslint-disable no-console */ - } - }) - .append('div') - .call(svgIcon('#iD-operation-merge')) - .attr('title', t('inspector.set_photo_from_viewer')); + + // check if mapillary image id field exist + if (!mapillary_image_ID_field.empty()) { + // insert id + const image = service.getActiveImage(); + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + utilGetSetValue(fieldInput,image.id); + + // trigger change at field + fieldInput.node().focus(); + fieldInput.node().dispatchEvent(changeEvent); + } else { + // open the Mapillary field + const comboboxInput = d3_select('.value.combobox-input'); + const EnterEvent = new KeyboardEvent('keydown',{keyCode : 13}); + utilGetSetValue(comboboxInput,'Mapillary Image ID'); + comboboxInput.node().focus(); + comboboxInput.node().dispatchEvent(EnterEvent); + + // insert id + const image = service.getActiveImage(); + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + utilGetSetValue(fieldInput,image.id); + + // trigger change at field + fieldInput.node().focus(); + fieldInput.node().dispatchEvent(changeEvent); + } } function buildResizeListener(target, eventName, dispatch, options) { From 437ecf956b27ac8651ad5017df530c81483455bf Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Wed, 24 Jan 2024 20:57:59 +0800 Subject: [PATCH 08/18] update open the viewer button logic 1 problem remain: if entity select and close the photoviewer, view button will not update class, still clickable though --- modules/ui/fields/input.js | 196 +++++++++++++++++++++++-------------- 1 file changed, 125 insertions(+), 71 deletions(-) diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index fdef4c9ff5..cc09b8d805 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -174,68 +174,8 @@ export function uiFieldText(field, context) { } else if (field.type === 'identifier' && field.urlFormat && field.pattern) { input.attr('type', 'text'); - if (field.id==='mapillary'){ - const service = services.mapillary; - // set button - mapillarySetButton = wrap.selectAll('.mapillary-set-current') - .data([0]) - .enter() - .append('button') - .attr('class', 'form-field-button mapillary-set-current') - .call(svgIcon('#fas-rotate')) - .attr('title', t('inspector.set_photo_from_field')) - .on('click', function(d3_event) { - d3_event.preventDefault(); - const image = service.getActiveImage(); - if (!image) return; - if (image.id===utilGetSetValue(input).trim()) return; - service - .ensureViewerLoaded(context) - .then(function() { - service - .showViewer(context) - .selectImage(context, image.id) - .initViewer(context); - utilGetSetValue(input, image.id); - change()(); - }); - }) - .merge(wrap.selectAll('.mapillary-set-current')) - .classed('disabled', () => { - const image = service.getActiveImage(); - if (!image) return true; - if (image.id===utilGetSetValue(input).trim()) return true; - }); - service.on('imageChanged', function() { - mapillarySetButton.classed('disabled', () => { - const image = service.getActiveImage(); - if (!image) return true; - if (image.id===utilGetSetValue(input).trim()) return true; - return false; - }); - }); - // view button - mapillaryViewButton = wrap.selectAll('.mapillary-show-view') - .data([0]) - .enter() - .append('button') - .attr('class', 'form-field-button mapillary-show-view') - .call(svgIcon('#fas-eye')) - .attr('title', t('inspector.show_photo_from_field')) - .on('click', function(d3_event) { - d3_event.preventDefault(); - if ( !utilGetSetValue(input).trim()) return; - service - .ensureViewerLoaded(context) - .then(function() { - service - .showViewer(context) - .selectImage(context, utilGetSetValue(input).trim()) - .initViewer(context); - }); - }) - .merge(wrap.selectAll('.mapillary-show-view')) - .classed('disabled', () => !utilGetSetValue(input).trim()); + if (field.id === 'mapillary') { + updateMapillaryImageIDField(); } outlinkButton = wrap.selectAll('.foreign-id-permalink') @@ -422,6 +362,111 @@ export function uiFieldText(field, context) { if (format) input.attr('placeholder', format); } + function updateMapillaryImageIDField() { + const service = services.mapillary; + // set button + mapillarySetButton = wrap.selectAll('.mapillary-set-current') + .data([0]); + + mapillarySetButton.enter() + .append('button') + .attr('class', 'form-field-button mapillary-set-current') + .call(svgIcon('#fas-rotate')) + .attr('title', t('inspector.set_photo_from_field')) + .on('click', function(d3_event) { + d3_event.preventDefault(); + const image = service.getActiveImage(); + if (!image) return; + if (image.id === utilGetSetValue(input).trim()) return; + service + .ensureViewerLoaded(context) + .then(function() { + utilGetSetValue(input, image.id); + change()(); + }); + mapillarySetButton.node().blur(); + }) + .merge(mapillarySetButton) + .classed('disabled', _debounce(() => { + const image = service.getActiveImage(); + if (!image) return true; + if (image.id === utilGetSetValue(input).trim()) return true; + return false; + }), 100); + + // view button + mapillaryViewButton = wrap.selectAll('.mapillary-show-view') + .data([0]); + + mapillaryViewButton.enter() + .append('button') + .attr('class', 'form-field-button mapillary-show-view') + .call(svgIcon('#fas-eye')) + .attr('title', t('inspector.show_photo_from_field')) + .on('click', function(d3_event) { + d3_event.preventDefault(); + const isHidden = context.container() + .select('.photoviewer') + .selectAll('.photo-wrapper.mly-wrapper.hide') + .size(); + + if (isHidden) { + if (!utilGetSetValue(input).trim()) return; + service + .ensureViewerLoaded(context) + .then(function() { + service + .showViewer(context) + .selectImage(context, utilGetSetValue(input).trim()) + .initViewer(context); + }); + } else { + if (!utilGetSetValue(input).trim()) return; + const image = service.getActiveImage(); + if (!image) return; + if (image.id === utilGetSetValue(input).trim()) return; + + service + .ensureViewerLoaded(context) + .then(function() { + service + .selectImage(context, utilGetSetValue(input).trim()) + .initViewer(context); + }); + } + mapillaryViewButton.node().blur(); + }) + .merge(mapillaryViewButton) + .classed('disabled', _debounce(() => { + if (!utilGetSetValue(input).trim()) return true; + const isHidden = context.container() + .select('.photoviewer') + .selectAll('.photo-wrapper.mly-wrapper.hide') + .size(); + if (isHidden) return false; + const image = service.getActiveImage(); + if (!image) return true; + if (image.id === utilGetSetValue(input).trim()) return true; + return false; + }, 100)); + + service.on('imageChanged', function() { + mapillarySetButton.classed('disabled', () => { + const image = service.getActiveImage(); + if (!image) return true; + if (image.id === utilGetSetValue(input).trim()) return true; + return false; + }); + mapillaryViewButton.classed('disabled', () => { + if (!utilGetSetValue(input).trim()) return true; + const image = service.getActiveImage(); + if (!image) return true; + if (image.id === utilGetSetValue(input).trim()) return true; + return false; + }); + }); + } + function validIdentifierValueForLink() { const value = utilGetSetValue(input).trim(); @@ -592,20 +637,29 @@ export function uiFieldText(field, context) { outlinkButton.classed('disabled', disabled); } - if (mapillaryViewButton && !mapillaryViewButton.empty()) { - mapillaryViewButton.classed('disabled', !utilGetSetValue(input).trim()); - } - if (mapillarySetButton && !mapillarySetButton.empty()) { const service = services.mapillary; mapillarySetButton.classed('disabled', () => { const image = service.getActiveImage(); if (!image) return true; - if (image.id===utilGetSetValue(input).trim()) { - return true; - } else { - return false; - } + if (image.id === utilGetSetValue(input).trim()) return true; + return false; + }); + } + + if (mapillaryViewButton && !mapillaryViewButton.empty()) { + const service = services.mapillary; + mapillaryViewButton.classed('disabled', () => { + if (!utilGetSetValue(input).trim()) return true; + const isHidden = context.container() + .select('.photoviewer') + .selectAll('.photo-wrapper.mly-wrapper.hide') + .size(); + if (isHidden) return false; + const image = service.getActiveImage(); + if (!image) return true; + if (image.id === utilGetSetValue(input).trim()) return true; + return false; }); } From 0ad46e0b72271e6f7f74c7835e6fcbb2c168c552 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Sat, 27 Jan 2024 15:14:27 +0800 Subject: [PATCH 09/18] Add error message in photoviewer when invalid id and update input.js --- css/60_photos.css | 12 ++++++++++++ data/core.yaml | 1 + modules/services/mapillary.js | 3 ++- modules/ui/fields/input.js | 11 +++++------ modules/ui/photoviewer.js | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index 1368ad40f9..71d9efa3ae 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -76,6 +76,18 @@ width: 100%; } +.photoviewer .error-handler { + border-radius: 0; + padding: 15px; + position: absolute; + left: 50%; + top: 75px; + transform: translate(-50%, -50%); + z-index: 50; + color: red; + font-size: 20px; +} + .photo-wrapper { width: 100%; diff --git a/data/core.yaml b/data/core.yaml index 9db7df56a6..a751f855b5 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -789,6 +789,7 @@ en: set_photo_from_viewer: "Store this photo on the currently selected map object" set_photo_from_field: "Use the currently displayed photo" show_photo_from_field: "Open image in viewer" + show_photo_from_field_error: "Invalid ID" background: title: Background description: Background Settings diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index fb64e1eab7..b8e46004fd 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -19,7 +19,7 @@ const trafficSignTileUrl = `${baseTileUrl}/mly_map_feature_traffic_sign/2/{z}/{x const viewercss = 'mapillary-js/mapillary.css'; const viewerjs = 'mapillary-js/mapillary.js'; const minZoom = 14; -const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged'); +const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged', 'error'); let _loadViewerPromise; let _mlyActiveImage; @@ -606,6 +606,7 @@ export default { _mlyViewer.moveTo(imageId) .catch(function(e) { console.error('mly3', e); // eslint-disable-line no-console + dispatch.call('error', undefined, e); }); } diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index cc09b8d805..d5157a7aad 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -384,7 +384,7 @@ export function uiFieldText(field, context) { utilGetSetValue(input, image.id); change()(); }); - mapillarySetButton.node().blur(); + mapillarySetButton.node()?.blur(); }) .merge(mapillarySetButton) .classed('disabled', _debounce(() => { @@ -412,13 +412,13 @@ export function uiFieldText(field, context) { if (isHidden) { if (!utilGetSetValue(input).trim()) return; + service .ensureViewerLoaded(context) .then(function() { service - .showViewer(context) .selectImage(context, utilGetSetValue(input).trim()) - .initViewer(context); + .showViewer(context); }); } else { if (!utilGetSetValue(input).trim()) return; @@ -430,11 +430,10 @@ export function uiFieldText(field, context) { .ensureViewerLoaded(context) .then(function() { service - .selectImage(context, utilGetSetValue(input).trim()) - .initViewer(context); + .selectImage(context, utilGetSetValue(input).trim()); }); } - mapillaryViewButton.node().blur(); + mapillaryViewButton.node()?.blur(); }) .merge(mapillaryViewButton) .classed('disabled', _debounce(() => { diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index d72baa4b66..1f523adc57 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -143,6 +143,24 @@ export function uiPhotoviewer(context) { } } + // error message handler + const errorHandler = selection + .append('div') + .attr('class', 'error-handler') + .append('div') + .style('opacity', '0'); + + + services.mapillary + .on('error', function(e) { + errorHandler + .text(t('inspector.show_photo_from_field_error')) + .style('opacity', '1') + .transition() + .duration(1000) + .style('opacity', '0'); + }); + function buildResizeListener(target, eventName, dispatch, options) { var resizeOnX = !!options.resizeOnX; From cc10898a58cdd5db49d832761c2899cf15d5f1e7 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:38:29 +0800 Subject: [PATCH 10/18] finish open the viewer button logic button disable class reacts to photoviewer close --- modules/ui/fields/input.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index d5157a7aad..1317d191b6 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -420,6 +420,7 @@ export function uiFieldText(field, context) { .selectImage(context, utilGetSetValue(input).trim()) .showViewer(context); }); + mapillaryViewButton.classed('disabled', true); } else { if (!utilGetSetValue(input).trim()) return; const image = service.getActiveImage(); @@ -457,6 +458,11 @@ export function uiFieldText(field, context) { return false; }); mapillaryViewButton.classed('disabled', () => { + const isHidden = context.container() + .select('.photoviewer') + .selectAll('.photo-wrapper.mly-wrapper.hide') + .size(); + if (isHidden) return false; if (!utilGetSetValue(input).trim()) return true; const image = service.getActiveImage(); if (!image) return true; From b597cc1341c99ff24734a2be6138e502e7abb6ab Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:30:52 +0800 Subject: [PATCH 11/18] refactor the code --- modules/ui/photoviewer.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 1f523adc57..c77e53fd6b 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -110,36 +110,31 @@ export function uiPhotoviewer(context) { }); function set_photo_from_mapillary_viewer() { - const mapillary_image_ID_field = d3_select('.wrap-form-field-mapillary'); - const changeEvent = new Event('change'); - const service = services.mapillary; - - // check if mapillary image id field exist - if (!mapillary_image_ID_field.empty()) { + function insertIdAndTriggerChangeAtField() { + const changeEvent = new Event('change'); + const service = services.mapillary; // insert id const image = service.getActiveImage(); const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - utilGetSetValue(fieldInput,image.id); + utilGetSetValue(fieldInput, image.id); // trigger change at field fieldInput.node().focus(); fieldInput.node().dispatchEvent(changeEvent); + } + // check if mapillary image id field exist + const mapillary_image_ID_field = d3_select('.wrap-form-field-mapillary'); + if (!mapillary_image_ID_field.empty()) { + insertIdAndTriggerChangeAtField(); } else { // open the Mapillary field const comboboxInput = d3_select('.value.combobox-input'); - const EnterEvent = new KeyboardEvent('keydown',{keyCode : 13}); - utilGetSetValue(comboboxInput,'Mapillary Image ID'); + const EnterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); + utilGetSetValue(comboboxInput, 'Mapillary Image ID'); comboboxInput.node().focus(); comboboxInput.node().dispatchEvent(EnterEvent); - // insert id - const image = service.getActiveImage(); - const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - utilGetSetValue(fieldInput,image.id); - - // trigger change at field - fieldInput.node().focus(); - fieldInput.node().dispatchEvent(changeEvent); + insertIdAndTriggerChangeAtField(); } } From 666bbbfbfcd5d06032c7b3ad3a26da67b0959bb8 Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:07:26 +0800 Subject: [PATCH 12/18] code style change --- modules/ui/photoviewer.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index c77e53fd6b..0daeb0bbd1 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -64,7 +64,7 @@ export function uiPhotoviewer(context) { // set_photo_from_viewer button context.features() // need fix : click sidebar not listen, maybe has more precise way to listen to - .on('change', function() { + .on('change', function () { const hash = utilStringQs(window.location.hash); let [serviceId, photoId] = []; let photo_overlay = []; @@ -78,24 +78,24 @@ export function uiPhotoviewer(context) { // check photoviewer open && photo and layer match && currently only support mapillary if (serviceId && photo_overlay.includes(serviceId) && serviceId === 'mapillary') { context.container() - .on('click.set_photo_from_viewer', function() { + .on('click.set_photo_from_viewer', function () { const inspector_wrap = d3_select('.inspector-wrap'); const editorPane = d3_select('.entity-editor-pane'); const button = selection.selectAll('.set_photo_from_viewer').data([0]); if (!inspector_wrap.classed('inspector-hidden') && - !editorPane.classed('hide') && + !editorPane.classed('hide') && context.mode().id === 'select') { - button.enter() - .append('button') - .attr('class', 'set_photo_from_viewer') - .on('click', function() { - if (serviceId === 'mapillary' && services.mapillary) { set_photo_from_mapillary_viewer(); } - }) - .append('div') - .call(svgIcon('#iD-operation-merge')) - .attr('title', t('inspector.set_photo_from_viewer')); + button.enter() + .append('button') + .attr('class', 'set_photo_from_viewer') + .on('click', function () { + if (serviceId === 'mapillary' && services.mapillary) { set_photo_from_mapillary_viewer(); } + }) + .append('div') + .call(svgIcon('#iD-operation-merge')) + .attr('title', t('inspector.set_photo_from_viewer')); } else { button.remove(); } @@ -147,7 +147,7 @@ export function uiPhotoviewer(context) { services.mapillary - .on('error', function(e) { + .on('error', function () { errorHandler .text(t('inspector.show_photo_from_field_error')) .style('opacity', '1') From 6b860b7da0e5c68f99f710b6862d0237251b47aa Mon Sep 17 00:00:00 2001 From: NaVis0mple <109860906+NaVis0mple@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:12:01 +0800 Subject: [PATCH 13/18] refactor the code '_' change to '-' --- css/60_photos.css | 2 +- modules/ui/photoviewer.js | 97 ++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index 71d9efa3ae..3eee4d15bc 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -33,7 +33,7 @@ z-index: 50; } -.photoviewer button.set_photo_from_viewer { +.photoviewer button.set-photo-from-viewer { border-radius: 0; padding: 5px; position: absolute; diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 0daeb0bbd1..9ba33e3f53 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -62,54 +62,55 @@ export function uiPhotoviewer(context) { ); // set_photo_from_viewer button - context.features() - // need fix : click sidebar not listen, maybe has more precise way to listen to - .on('change', function () { - const hash = utilStringQs(window.location.hash); - let [serviceId, photoId] = []; - let photo_overlay = []; - if (hash.photo) { - [serviceId, photoId] = hash.photo.split('/'); - } + context.container().on('click.setPhotoFromViewer', setPhotoFromViewerButton); + + function setPhotoFromViewerButton() { + const inspectorWrap = d3_select('.inspector-wrap'); + const editorPane = d3_select('.entity-editor-pane'); + const presetPane = d3_select('.preset-list-pane'); + const button = selection.selectAll('.set-photo-from-viewer').data([0]); + const hash = utilStringQs(window.location.hash); + let [serviceId, photoId] = []; + let photo_overlay = []; + if (hash.photo) { + [serviceId, photoId] = hash.photo.split('/'); + } - if (hash.photo_overlay) { - photo_overlay = hash.photo_overlay.split(','); - } - // check photoviewer open && photo and layer match && currently only support mapillary - if (serviceId && photo_overlay.includes(serviceId) && serviceId === 'mapillary') { - context.container() - .on('click.set_photo_from_viewer', function () { - const inspector_wrap = d3_select('.inspector-wrap'); - const editorPane = d3_select('.entity-editor-pane'); - - const button = selection.selectAll('.set_photo_from_viewer').data([0]); - - if (!inspector_wrap.classed('inspector-hidden') && - !editorPane.classed('hide') && - context.mode().id === 'select') { - button.enter() - .append('button') - .attr('class', 'set_photo_from_viewer') - .on('click', function () { - if (serviceId === 'mapillary' && services.mapillary) { set_photo_from_mapillary_viewer(); } - }) - .append('div') - .call(svgIcon('#iD-operation-merge')) - .attr('title', t('inspector.set_photo_from_viewer')); - } else { - button.remove(); - } - }); - } else { - // remove listener and button - context.container() - .on('click.set_photo_from_viewer', null); - const button = selection.selectAll('.set_photo_from_viewer'); - button.remove(); - } - }); + if (hash.photo_overlay) { + photo_overlay = hash.photo_overlay.split(','); + } + + // check photoviewer open && photo and layer match && currently only support mapillary && editorpane open + if (serviceId && + photo_overlay.includes(serviceId) && + serviceId === 'mapillary' && + !inspectorWrap.classed('inspector-hidden') && + !editorPane.classed('hide') && + presetPane.classed('hide') && + context.mode().id === 'select') { + buttonCreate(); + context.features().on('change.setPhotoFromViewerButtonCreate', buttonCreate); + } else { + // remove listener and button + context.features().on('change.setPhotoFromViewerButtonCreate', null); + const button = selection.selectAll('.set-photo-from-viewer'); + button.remove(); + } + + function buttonCreate() { + button.enter() + .append('button') + .attr('class', 'set-photo-from-viewer') + .on('click', function () { + if (serviceId === 'mapillary' && services.mapillary) { setPhotoFromMapillaryViewer(); } + }) + .append('div') + .call(svgIcon('#iD-operation-merge')) + .attr('title', t('inspector.set_photo_from_viewer')); + } + } - function set_photo_from_mapillary_viewer() { + function setPhotoFromMapillaryViewer() { function insertIdAndTriggerChangeAtField() { const changeEvent = new Event('change'); const service = services.mapillary; @@ -123,8 +124,8 @@ export function uiPhotoviewer(context) { fieldInput.node().dispatchEvent(changeEvent); } // check if mapillary image id field exist - const mapillary_image_ID_field = d3_select('.wrap-form-field-mapillary'); - if (!mapillary_image_ID_field.empty()) { + const mapillaryImageIDField = d3_select('.wrap-form-field-mapillary'); + if (!mapillaryImageIDField.empty()) { insertIdAndTriggerChangeAtField(); } else { // open the Mapillary field From 94e32a85b03b8462feb2bd9963ad9f515e4c1cdf Mon Sep 17 00:00:00 2001 From: laigyu Date: Sun, 7 Apr 2024 22:04:22 +0800 Subject: [PATCH 14/18] revert field button --- data/core.yaml | 2 - modules/ui/fields/input.js | 145 +------------------------------------ modules/ui/photoviewer.js | 80 ++++++++++++-------- 3 files changed, 49 insertions(+), 178 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index d30a2fc4ff..b2d8cb809a 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -789,8 +789,6 @@ en: max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." set_photo_from_viewer: "Store this photo on the currently selected map object" - set_photo_from_field: "Use the currently displayed photo" - show_photo_from_field: "Open image in viewer" show_photo_from_field_error: "Invalid ID" background: title: Background diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 1317d191b6..c5061b5ea0 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -13,7 +13,6 @@ import { isColourValid } from '../../osm/tags'; import { uiLengthIndicator } from '..'; import { uiTooltip } from '../tooltip'; import { isEqual } from 'lodash-es'; -import { services } from '../../services'; export { uiFieldText as uiFieldColour, @@ -31,8 +30,6 @@ export function uiFieldText(field, context) { var dispatch = d3_dispatch('change'); var input = d3_select(null); var outlinkButton = d3_select(null); - var mapillaryViewButton = d3_select(null); - var mapillarySetButton = d3_select(null); var wrap = d3_select(null); var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue()); var _entityIDs = []; @@ -172,12 +169,8 @@ export function uiFieldText(field, context) { change()(); }); } else if (field.type === 'identifier' && field.urlFormat && field.pattern) { - input.attr('type', 'text'); - - if (field.id === 'mapillary') { - updateMapillaryImageIDField(); - } + input.attr('type', 'text'); outlinkButton = wrap.selectAll('.foreign-id-permalink') .data([0]); @@ -362,116 +355,6 @@ export function uiFieldText(field, context) { if (format) input.attr('placeholder', format); } - function updateMapillaryImageIDField() { - const service = services.mapillary; - // set button - mapillarySetButton = wrap.selectAll('.mapillary-set-current') - .data([0]); - - mapillarySetButton.enter() - .append('button') - .attr('class', 'form-field-button mapillary-set-current') - .call(svgIcon('#fas-rotate')) - .attr('title', t('inspector.set_photo_from_field')) - .on('click', function(d3_event) { - d3_event.preventDefault(); - const image = service.getActiveImage(); - if (!image) return; - if (image.id === utilGetSetValue(input).trim()) return; - service - .ensureViewerLoaded(context) - .then(function() { - utilGetSetValue(input, image.id); - change()(); - }); - mapillarySetButton.node()?.blur(); - }) - .merge(mapillarySetButton) - .classed('disabled', _debounce(() => { - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }), 100); - - // view button - mapillaryViewButton = wrap.selectAll('.mapillary-show-view') - .data([0]); - - mapillaryViewButton.enter() - .append('button') - .attr('class', 'form-field-button mapillary-show-view') - .call(svgIcon('#fas-eye')) - .attr('title', t('inspector.show_photo_from_field')) - .on('click', function(d3_event) { - d3_event.preventDefault(); - const isHidden = context.container() - .select('.photoviewer') - .selectAll('.photo-wrapper.mly-wrapper.hide') - .size(); - - if (isHidden) { - if (!utilGetSetValue(input).trim()) return; - - service - .ensureViewerLoaded(context) - .then(function() { - service - .selectImage(context, utilGetSetValue(input).trim()) - .showViewer(context); - }); - mapillaryViewButton.classed('disabled', true); - } else { - if (!utilGetSetValue(input).trim()) return; - const image = service.getActiveImage(); - if (!image) return; - if (image.id === utilGetSetValue(input).trim()) return; - - service - .ensureViewerLoaded(context) - .then(function() { - service - .selectImage(context, utilGetSetValue(input).trim()); - }); - } - mapillaryViewButton.node()?.blur(); - }) - .merge(mapillaryViewButton) - .classed('disabled', _debounce(() => { - if (!utilGetSetValue(input).trim()) return true; - const isHidden = context.container() - .select('.photoviewer') - .selectAll('.photo-wrapper.mly-wrapper.hide') - .size(); - if (isHidden) return false; - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }, 100)); - - service.on('imageChanged', function() { - mapillarySetButton.classed('disabled', () => { - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }); - mapillaryViewButton.classed('disabled', () => { - const isHidden = context.container() - .select('.photoviewer') - .selectAll('.photo-wrapper.mly-wrapper.hide') - .size(); - if (isHidden) return false; - if (!utilGetSetValue(input).trim()) return true; - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }); - }); - } - function validIdentifierValueForLink() { const value = utilGetSetValue(input).trim(); @@ -642,32 +525,6 @@ export function uiFieldText(field, context) { outlinkButton.classed('disabled', disabled); } - if (mapillarySetButton && !mapillarySetButton.empty()) { - const service = services.mapillary; - mapillarySetButton.classed('disabled', () => { - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }); - } - - if (mapillaryViewButton && !mapillaryViewButton.empty()) { - const service = services.mapillary; - mapillaryViewButton.classed('disabled', () => { - if (!utilGetSetValue(input).trim()) return true; - const isHidden = context.container() - .select('.photoviewer') - .selectAll('.photo-wrapper.mly-wrapper.hide') - .size(); - if (isHidden) return false; - const image = service.getActiveImage(); - if (!image) return true; - if (image.id === utilGetSetValue(input).trim()) return true; - return false; - }); - } - if (!isMixed) { _lengthIndicator.update(tags[field.key]); } diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 9ba33e3f53..16bd7b62eb 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -65,48 +65,64 @@ export function uiPhotoviewer(context) { context.container().on('click.setPhotoFromViewer', setPhotoFromViewerButton); function setPhotoFromViewerButton() { - const inspectorWrap = d3_select('.inspector-wrap'); - const editorPane = d3_select('.entity-editor-pane'); - const presetPane = d3_select('.preset-list-pane'); - const button = selection.selectAll('.set-photo-from-viewer').data([0]); - const hash = utilStringQs(window.location.hash); - let [serviceId, photoId] = []; - let photo_overlay = []; - if (hash.photo) { - [serviceId, photoId] = hash.photo.split('/'); - } + services.mapillary.on('imageChanged', function() { + let activeImageId; + const layers = context.layers(); + function layerStatus(which) { + const layer = layers.layer(which); + return layer.enabled(); + } - if (hash.photo_overlay) { - photo_overlay = hash.photo_overlay.split(','); - } + const hash = utilStringQs(window.location.hash); + let serviceId; + if (hash.photo) { + let result = hash.photo.split('/'); + serviceId = result[0]; + } + activeImageId = services.mapillary.getActiveImage()?.id; + // check layer enabled && currently only support mapillary && editorpane open + if (layerStatus('mapillary') && serviceId === 'mapillary' && paneCheck()) { + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + if (!fieldInput.empty()) { + if (activeImageId === utilGetSetValue(fieldInput)) return buttonRemove(); + } + const buttonWithoutClickEvent = buttonCreate(); + buttonWithoutClickEvent.on('click', function () { + setPhotoFromMapillaryViewer(); + buttonRemove(); + }); + } else { + buttonRemove(); + } + }); - // check photoviewer open && photo and layer match && currently only support mapillary && editorpane open - if (serviceId && - photo_overlay.includes(serviceId) && - serviceId === 'mapillary' && - !inspectorWrap.classed('inspector-hidden') && - !editorPane.classed('hide') && - presetPane.classed('hide') && - context.mode().id === 'select') { - buttonCreate(); - context.features().on('change.setPhotoFromViewerButtonCreate', buttonCreate); - } else { - // remove listener and button - context.features().on('change.setPhotoFromViewerButtonCreate', null); - const button = selection.selectAll('.set-photo-from-viewer'); - button.remove(); + function paneCheck() { + const inspectorWrap = d3_select('.inspector-wrap'); + const editorPane = d3_select('.entity-editor-pane'); + const presetPane = d3_select('.preset-list-pane'); + + return !inspectorWrap.classed('inspector-hidden') && + !editorPane.classed('hide') && + presetPane.classed('hide') && + context.mode().id === 'select'; } + function buttonCreate() { - button.enter() + const button = selection.selectAll('.set-photo-from-viewer').data([0]); + const buttonEnter = button.enter() .append('button') .attr('class', 'set-photo-from-viewer') - .on('click', function () { - if (serviceId === 'mapillary' && services.mapillary) { setPhotoFromMapillaryViewer(); } - }) .append('div') .call(svgIcon('#iD-operation-merge')) .attr('title', t('inspector.set_photo_from_viewer')); + + return buttonEnter; + } + + function buttonRemove() { + const button = selection.selectAll('.set-photo-from-viewer').data([0]); + button?.remove(); } } From d448b586ed87c8d6a2fa3b3915e79f88b2cc3dcf Mon Sep 17 00:00:00 2001 From: laigyu Date: Sun, 7 Apr 2024 23:19:41 +0800 Subject: [PATCH 15/18] revert error handler --- css/60_photos.css | 12 ------------ data/core.yaml | 1 - modules/services/mapillary.js | 3 +-- modules/ui/photoviewer.js | 18 ------------------ 4 files changed, 1 insertion(+), 33 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index 1332dcdb0d..2794c48d66 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -76,18 +76,6 @@ width: 100%; } -.photoviewer .error-handler { - border-radius: 0; - padding: 15px; - position: absolute; - left: 50%; - top: 75px; - transform: translate(-50%, -50%); - z-index: 50; - color: red; - font-size: 20px; -} - .photo-wrapper { width: 100%; diff --git a/data/core.yaml b/data/core.yaml index b2d8cb809a..a60d49d9b7 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -789,7 +789,6 @@ en: max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." set_photo_from_viewer: "Store this photo on the currently selected map object" - show_photo_from_field_error: "Invalid ID" background: title: Background description: Background Settings diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index b8e46004fd..fb64e1eab7 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -19,7 +19,7 @@ const trafficSignTileUrl = `${baseTileUrl}/mly_map_feature_traffic_sign/2/{z}/{x const viewercss = 'mapillary-js/mapillary.css'; const viewerjs = 'mapillary-js/mapillary.js'; const minZoom = 14; -const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged', 'error'); +const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged'); let _loadViewerPromise; let _mlyActiveImage; @@ -606,7 +606,6 @@ export default { _mlyViewer.moveTo(imageId) .catch(function(e) { console.error('mly3', e); // eslint-disable-line no-console - dispatch.call('error', undefined, e); }); } diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 16bd7b62eb..aa93b5e59e 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -155,24 +155,6 @@ export function uiPhotoviewer(context) { } } - // error message handler - const errorHandler = selection - .append('div') - .attr('class', 'error-handler') - .append('div') - .style('opacity', '0'); - - - services.mapillary - .on('error', function () { - errorHandler - .text(t('inspector.show_photo_from_field_error')) - .style('opacity', '1') - .transition() - .duration(1000) - .style('opacity', '0'); - }); - function buildResizeListener(target, eventName, dispatch, options) { var resizeOnX = !!options.resizeOnX; From 6b8b41c2ff0e6ac4274cb84573e17fd4e6236000 Mon Sep 17 00:00:00 2001 From: laigyu Date: Mon, 8 Apr 2024 18:22:58 +0800 Subject: [PATCH 16/18] tooltip style change --- modules/ui/photoviewer.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index aa93b5e59e..0651a07fa6 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -8,6 +8,7 @@ import { svgIcon } from '../svg/icon'; import { utilGetDimensions } from '../util/dimensions'; import { utilRebind, utilGetSetValue, utilStringQs } from '../util'; import { services } from '../services'; +import { uiTooltip } from './tooltip'; export function uiPhotoviewer(context) { @@ -115,7 +116,15 @@ export function uiPhotoviewer(context) { .attr('class', 'set-photo-from-viewer') .append('div') .call(svgIcon('#iD-operation-merge')) - .attr('title', t('inspector.set_photo_from_viewer')); + .call(uiTooltip() + .title(() => t.append('inspector.set_photo_from_viewer')) + .placement('right') + ); + + buttonEnter + .select('.tooltip') + .classed('dark', true) + .style('width', '300px'); return buttonEnter; } From 2325de466200da0a59ef6942f2a35c54b9fa6456 Mon Sep 17 00:00:00 2001 From: laigyu Date: Fri, 12 Apr 2024 18:37:26 +0800 Subject: [PATCH 17/18] update butotn logic --- modules/services/mapillary.js | 11 +++ modules/ui/photoviewer.js | 166 ++++++++++++++++++++++------------ 2 files changed, 119 insertions(+), 58 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index fb64e1eab7..8e0c2fe321 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -30,6 +30,7 @@ let _mlyShowFeatureDetections = false; let _mlyShowSignDetections = false; let _mlyViewer; let _mlyViewerFilter = ['all']; +let _isViewerOpen = false; // Load all data for the specified type from Mapillary vector tiles @@ -478,6 +479,8 @@ export default { _mlyViewer.resize(); } + _isViewerOpen = true; + return this; }, @@ -504,10 +507,18 @@ export default { dispatch.call('loadedMapFeatures'); dispatch.call('loadedSigns'); + _isViewerOpen = false; + return this.setStyles(context, null); }, + // Get viewer status + isViewerOpen: function() { + return _isViewerOpen; + }, + + // Update the URL with current image id updateUrlImage: function(imageId) { if (!window.mocha) { diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 0651a07fa6..358ac75cab 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -63,39 +63,103 @@ export function uiPhotoviewer(context) { ); // set_photo_from_viewer button - context.container().on('click.setPhotoFromViewer', setPhotoFromViewerButton); + let _selectedID = context.selectedIDs(); + context.container().on('click.setPhotoFromViewer', function(e) { + setPhotoFromViewerButton(e); + }); + + function setPhotoFromViewerButton(e) { + services.mapillary.ensureViewerLoaded(context).then(() => { + if (e.target.closest('.mly-wrapper')) { + services.mapillary.on('imageChanged.set', function() { + MapillaryButtonDisabledUpdate(); + }); + return; + } - function setPhotoFromViewerButton() { - services.mapillary.on('imageChanged', function() { - let activeImageId; - const layers = context.layers(); - function layerStatus(which) { - const layer = layers.layer(which); - return layer.enabled(); + if (!services.mapillary.isViewerOpen()) return; + + if (context.mode().id !== 'select' || !paneCheck() || !(layerStatus('mapillary') && getServiceId() === 'mapillary')) { + buttonRemove(); + } else { + if (selection.select('.set-photo-from-viewer').empty()) { + const button = buttonCreate(); + button.on('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + setMapillaryPhotoId(); + buttonDisable(true); + }); + MapillaryButtonDisabledUpdate(); + } else { + if (isSelectedIDChanged()) { + MapillaryButtonDisabledUpdate(); + updateSelectedID(); + } + } + } + + function MapillaryButtonDisabledUpdate() { + let activeImageId = services.mapillary.getActiveImage()?.id; + + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + if (!fieldInput.empty()) { + if (activeImageId === utilGetSetValue(fieldInput)) { + buttonDisable(true); + } else { + buttonDisable(false); + } + } else { + buttonDisable(false); + } + } + + function setMapillaryPhotoId() { + function insertIdAndTriggerChangeAtField() { + const changeEvent = new Event('change'); + const service = services.mapillary; + // insert id + const image = service.getActiveImage(); + const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); + utilGetSetValue(fieldInput, image.id); + + // trigger change at field + fieldInput.node().focus(); + fieldInput.node().dispatchEvent(changeEvent); + } + // check if mapillary image id field exist + const mapillaryImageIDField = d3_select('.wrap-form-field-mapillary'); + if (!mapillaryImageIDField.empty()) { + insertIdAndTriggerChangeAtField(); + } else { + // open the Mapillary field + const comboboxInput = d3_select('.value.combobox-input'); + const EnterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); + utilGetSetValue(comboboxInput, 'Mapillary Image ID'); + comboboxInput.node().focus(); + comboboxInput.node().dispatchEvent(EnterEvent); + + insertIdAndTriggerChangeAtField(); + } } + }); + + function layerStatus(which) { + const layers = context.layers(); + const layer = layers.layer(which); + return layer.enabled(); + } + + function getServiceId() { const hash = utilStringQs(window.location.hash); let serviceId; if (hash.photo) { let result = hash.photo.split('/'); serviceId = result[0]; } - activeImageId = services.mapillary.getActiveImage()?.id; - // check layer enabled && currently only support mapillary && editorpane open - if (layerStatus('mapillary') && serviceId === 'mapillary' && paneCheck()) { - const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - if (!fieldInput.empty()) { - if (activeImageId === utilGetSetValue(fieldInput)) return buttonRemove(); - } - const buttonWithoutClickEvent = buttonCreate(); - buttonWithoutClickEvent.on('click', function () { - setPhotoFromMapillaryViewer(); - buttonRemove(); - }); - } else { - buttonRemove(); - } - }); + return serviceId; + } function paneCheck() { const inspectorWrap = d3_select('.inspector-wrap'); @@ -103,26 +167,35 @@ export function uiPhotoviewer(context) { const presetPane = d3_select('.preset-list-pane'); return !inspectorWrap.classed('inspector-hidden') && - !editorPane.classed('hide') && - presetPane.classed('hide') && - context.mode().id === 'select'; + !editorPane.classed('hide') && + presetPane.classed('hide'); } + function isSelectedIDChanged() { + const currentSelectId = context.selectedIDs(); + if (JSON.stringify(_selectedID) === JSON.stringify(currentSelectId)) { + return false; + } else { + return true; + } + } + + function updateSelectedID() { + _selectedID = context.selectedIDs(); + } function buttonCreate() { const button = selection.selectAll('.set-photo-from-viewer').data([0]); const buttonEnter = button.enter() .append('button') .attr('class', 'set-photo-from-viewer') - .append('div') .call(svgIcon('#iD-operation-merge')) .call(uiTooltip() .title(() => t.append('inspector.set_photo_from_viewer')) .placement('right') ); - buttonEnter - .select('.tooltip') + buttonEnter.select('.tooltip') .classed('dark', true) .style('width', '300px'); @@ -131,36 +204,13 @@ export function uiPhotoviewer(context) { function buttonRemove() { const button = selection.selectAll('.set-photo-from-viewer').data([0]); - button?.remove(); + button.remove(); } - } - function setPhotoFromMapillaryViewer() { - function insertIdAndTriggerChangeAtField() { - const changeEvent = new Event('change'); - const service = services.mapillary; - // insert id - const image = service.getActiveImage(); - const fieldInput = d3_select('.wrap-form-field-mapillary .identifier'); - utilGetSetValue(fieldInput, image.id); - - // trigger change at field - fieldInput.node().focus(); - fieldInput.node().dispatchEvent(changeEvent); - } - // check if mapillary image id field exist - const mapillaryImageIDField = d3_select('.wrap-form-field-mapillary'); - if (!mapillaryImageIDField.empty()) { - insertIdAndTriggerChangeAtField(); - } else { - // open the Mapillary field - const comboboxInput = d3_select('.value.combobox-input'); - const EnterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); - utilGetSetValue(comboboxInput, 'Mapillary Image ID'); - comboboxInput.node().focus(); - comboboxInput.node().dispatchEvent(EnterEvent); - - insertIdAndTriggerChangeAtField(); + function buttonDisable(disabled) { + const button = selection.selectAll('.set-photo-from-viewer').data([0]); + button.attr('disabled', disabled ? 'true' : null); + button.classed('disabled', disabled); } } From b1ff86cd298a9a948223e56c9fa1b7eba92df54a Mon Sep 17 00:00:00 2001 From: laigyu Date: Mon, 15 Apr 2024 14:54:43 +0800 Subject: [PATCH 18/18] tooltip update --- data/core.yaml | 4 +++- modules/ui/photoviewer.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/data/core.yaml b/data/core.yaml index a60d49d9b7..e1cdc636c2 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -788,7 +788,9 @@ en: inch: in max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." - set_photo_from_viewer: "Store this photo on the currently selected map object" + set_photo_from_viewer: + enable: "Tag this photo id on the currently selected map object" + disable: "This image is already tagged on this feature" background: title: Background description: Background Settings diff --git a/modules/ui/photoviewer.js b/modules/ui/photoviewer.js index 358ac75cab..06b79cc8b2 100644 --- a/modules/ui/photoviewer.js +++ b/modules/ui/photoviewer.js @@ -211,6 +211,23 @@ export function uiPhotoviewer(context) { const button = selection.selectAll('.set-photo-from-viewer').data([0]); button.attr('disabled', disabled ? 'true' : null); button.classed('disabled', disabled); + button.call(uiTooltip().destroyAny); + if (disabled) { + button.call(uiTooltip() + .title(() => t.append('inspector.set_photo_from_viewer.disable')) + .placement('right') + ); + } else { + button.call(uiTooltip() + .title(() => t.append('inspector.set_photo_from_viewer.enable')) + .placement('right') + ); + } + + button.select('.tooltip') + .classed('dark', true) + .style('width', '300px'); + } }