From e3ba55e682583f0b1a2eb61f897883eb3fc6fa93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Pimi=C3=A4?= Date: Fri, 23 Aug 2019 15:27:23 +0300 Subject: [PATCH 1/2] GeoJSON message bar in the itinerary view --- app/component/SummaryPage.js | 16 +++++++++ app/component/map/MapWithTracking.js | 53 ++++++++++++++-------------- app/store/MessageStore.js | 1 + app/util/messageUtils.js | 32 +++++++++++++++++ 4 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 app/util/messageUtils.js diff --git a/app/component/SummaryPage.js b/app/component/SummaryPage.js index fe01621bd5..f764d0aa48 100644 --- a/app/component/SummaryPage.js +++ b/app/component/SummaryPage.js @@ -33,6 +33,8 @@ import ComponentUsageExample from './ComponentUsageExample'; import exampleData from './data/SummaryPage.ExampleData'; import { isBrowser } from '../util/browser'; import { itineraryHasCancelation } from '../util/alertUtils'; +import triggerMessage from '../util/messageUtils'; +import MessageStore from '../store/MessageStore'; export const ITINERARYFILTERING_DEFAULT = 1.5; @@ -88,6 +90,7 @@ class SummaryPage extends React.Component { config: PropTypes.object, executeAction: PropTypes.func.isRequired, headers: PropTypes.object.isRequired, + getStore: PropTypes.func, }; static propTypes = { @@ -182,6 +185,19 @@ class SummaryPage extends React.Component { } = this.context; const itineraries = (plan && plan.itineraries) || []; const activeIndex = getActiveIndex(location, itineraries); + triggerMessage( + from.lat, + from.lon, + this.context, + this.context.getStore(MessageStore).getMessages(), + ); + + triggerMessage( + to.lat, + to.lon, + this.context, + this.context.getStore(MessageStore).getMessages(), + ); const leafletObjs = sortBy( itineraries.map((itinerary, i) => ( diff --git a/app/component/map/MapWithTracking.js b/app/component/map/MapWithTracking.js index 3355c342e7..fd35e78f3b 100644 --- a/app/component/map/MapWithTracking.js +++ b/app/component/map/MapWithTracking.js @@ -19,8 +19,7 @@ import { stopRealTimeClient, changeRealTimeClientTopics, } from '../../action/realTimeClientAction'; -import { findFeatures } from '../../util/geo-utils'; -import { updateMessage } from '../../action/MessageActions'; +import triggerMessage from '../../util/messageUtils'; const DEFAULT_ZOOM = 12; const FOCUS_ZOOM = 16; @@ -138,7 +137,7 @@ class MapWithTrackingStateHandler extends React.Component { const lon = this.state.focusOnDestination ? this.state.destination.lon : this.state.origin.lon; - await this.triggerMessage(lat, lon); + await triggerMessage(lat, lon, this.context, this.props.messages); } } @@ -152,7 +151,12 @@ class MapWithTrackingStateHandler extends React.Component { !this.state.origin.gps) // current position selected ) { this.usePosition(newProps.origin); - this.triggerMessage(newProps.origin.lat, newProps.origin.lon); + triggerMessage( + newProps.origin.lat, + newProps.origin.lon, + this.context, + this.props.messages, + ); } else if ( // "current position selected" newProps.destination.lat !== null && @@ -163,7 +167,12 @@ class MapWithTrackingStateHandler extends React.Component { !this.state.destination.gps) // current position selected ) { this.usePosition(newProps.destination); - this.triggerMessage(newProps.destination.lat, newProps.destination.lon); + triggerMessage( + newProps.destination.lat, + newProps.destination.lon, + this.context, + this.props.messages, + ); } else if ( // "poi selected" !newProps.origin.gps && @@ -173,7 +182,12 @@ class MapWithTrackingStateHandler extends React.Component { newProps.origin.lon != null ) { this.useOrigin(newProps.origin); - this.triggerMessage(newProps.origin.lat, newProps.origin.lon); + triggerMessage( + newProps.origin.lat, + newProps.origin.lon, + this.context, + this.props.messages, + ); } else if ( // destination selected without poi !newProps.destination.gps && @@ -183,7 +197,12 @@ class MapWithTrackingStateHandler extends React.Component { newProps.destination.lon != null ) { this.useDestination(newProps.destination); - this.triggerMessage(newProps.destination.lat, newProps.destination.lon); + triggerMessage( + newProps.destination.lat, + newProps.destination.lon, + this.context, + this.props.messages, + ); } } @@ -257,26 +276,6 @@ class MapWithTrackingStateHandler extends React.Component { return geoHashes; }; - triggerMessage = (lat, lon) => { - const messages = this.props.messages.filter( - msg => !msg.shouldTrigger && msg.content && msg.geoJson, - ); - messages.forEach(msg => { - return new Promise(resolve => { - resolve(this.props.getGeoJsonData(msg.geoJson)); - }).then(value => { - const data = findFeatures( - { lat, lon }, - (value && value.data && value.data.features) || [], - ); - if (data.length > 0) { - msg.shouldTrigger = true; // eslint-disable-line no-param-reassign - this.context.executeAction(updateMessage, msg); - } - }); - }); - }; - startClient() { const { realTime, defaultEndpoint } = this.props.config; const agency = this.props.config.feedIds[0]; diff --git a/app/store/MessageStore.js b/app/store/MessageStore.js index f315df6649..0050f57965 100644 --- a/app/store/MessageStore.js +++ b/app/store/MessageStore.js @@ -89,6 +89,7 @@ class MessageStore extends Store { } else { message.shouldTrigger = true; } + this.messages.set(message.id, message); this.emitChange(); }; diff --git a/app/util/messageUtils.js b/app/util/messageUtils.js new file mode 100644 index 0000000000..1a56e2744b --- /dev/null +++ b/app/util/messageUtils.js @@ -0,0 +1,32 @@ +import { findFeatures } from './geo-utils'; +import { updateMessage } from '../action/MessageActions'; +import GeoJsonStore from '../store/GeoJsonStore'; + +/** + * Checks if the user is inside an area polygon featured in a message + * + * @param {*} lat The latitude of the position + * @param {*} lon The longitude of the position + * @param {*} context the context of the component + * @param {*} messagesToCheck the messages to be checked + */ +export default function triggerMessage(lat, lon, context, messagesToCheck) { + const { getGeoJsonData } = context.getStore(GeoJsonStore); + const messages = messagesToCheck.filter( + msg => !msg.shouldTrigger && msg.content && msg.geoJson, + ); + messages.forEach(msg => { + return new Promise(resolve => { + resolve(getGeoJsonData(msg.geoJson)); + }).then(value => { + const data = findFeatures( + { lat, lon }, + (value && value.data && value.data.features) || [], + ); + if (data.length > 0) { + msg.shouldTrigger = true; // eslint-disable-line no-param-reassign + context.executeAction(updateMessage, msg); + } + }); + }); +} From 54b35656bdfebbf126d2820c230ebf3cadb20311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Pimi=C3=A4?= Date: Thu, 29 Aug 2019 10:05:29 +0300 Subject: [PATCH 2/2] Added unit tests --- test/unit/util/messageUtils.test.js | 72 +++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 test/unit/util/messageUtils.test.js diff --git a/test/unit/util/messageUtils.test.js b/test/unit/util/messageUtils.test.js new file mode 100644 index 0000000000..c2e3ef887f --- /dev/null +++ b/test/unit/util/messageUtils.test.js @@ -0,0 +1,72 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; +import { mockContext } from '../helpers/mock-context'; +import triggerMessage from '../../../app/util/messageUtils'; +import MessageStore from '../../../app/store/MessageStore'; + +describe('triggerMessage', () => { + it("should call the update message action if position is found from the message's GeoJSON", async () => { + const store = new MessageStore(); + + const lon = 24.933973; + const lat = 60.199017; + + const config = { + staticMessages: [ + { + id: '1', + content: { + en: [ + { + type: 'text', + content: 'bar', + }, + ], + }, + shouldTrigger: false, + priority: -1, + }, + ], + }; + + await store.addConfigMessages(config); + + const msgs = [ + { + dataURI: '', + geoJson: + 'data:application/json;base64,ewogICJ0eXBlIjogIkZlYXR1cmVDb2xsZWN0aW9uIiwKICAiZmVhdHVyZXMiOiBbCiAgICB7CiAgICAgICJ0eXBlIjogIkZlYXR1cmUiLAogICAgICAiZ2VvbWV0cnkiOiB7CiAgICAgICAgInR5cGUiOiAiUG9seWdvbiIsCiAgICAgICAgImNvb3JkaW5hdGVzIjogWwogICAgICAgICAgWwogICAgICAgICAgICBbCiAgICAgICAgICAgICAgMjQuOTMxNDAzNjQ4NTg2NTI0LAogICAgICAgICAgICAgIDYwLjIwMDc2NjQ1MDk1NDE4CiAgICAgICAgICAgIF0sCiAgICAgICAgICAgIFsKICAgICAgICAgICAgICAyNC45Mjg1MDY4NjI4NTA0NCwKICAgICAgICAgICAgICA2MC4xOTk4NjAwMjc3NDA5NTQKICAgICAgICAgICAgXSwKICAgICAgICAgICAgWwogICAgICAgICAgICAgIDI0LjkyOTQ1MTAwMDQyMzY4MywKICAgICAgICAgICAgICA2MC4xOTcwOTc5NDg1MDMyCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgIFsKICAgICAgICAgICAgICAyNC45MzQ1NzkzODQwNjAxNTcsCiAgICAgICAgICAgICAgNjAuMTk1MTc4MjE5ODIxNzEKICAgICAgICAgICAgXSwKICAgICAgICAgICAgWwogICAgICAgICAgICAgIDI0Ljk0MzE2MjQ1MjkwNzgxNCwKICAgICAgICAgICAgICA2MC4xOTYyNTU0MTQ3NDIzMgogICAgICAgICAgICBdLAogICAgICAgICAgICBbCiAgICAgICAgICAgICAgMjQuOTQ0NDA2OTk3ODkwNzI0LAogICAgICAgICAgICAgIDYwLjIwMDIzMzI2Mzg2MTEzCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgIFsKICAgICAgICAgICAgICAyNC45Mzg2OTkyNTcxMDcwMzIsCiAgICAgICAgICAgICAgNjAuMjAxNTg3NTQyMTMyNzMKICAgICAgICAgICAgXSwKICAgICAgICAgICAgWwogICAgICAgICAgICAgIDI0LjkzMTQwMzY0ODU4NjUyNCwKICAgICAgICAgICAgICA2MC4yMDA3NjY0NTA5NTQxOAogICAgICAgICAgICBdCiAgICAgICAgICBdCiAgICAgICAgXQogICAgICB9LAogICAgICAicHJvcGVydGllcyI6IHt9CiAgICB9CiAgXQp9', + content: { + fi: [ + { + content: 'Geojson testi: olet Pasilassa', + type: 'heading', + }, + ], + en: [ + { + content: 'Geojson test: you are in Pasila', + type: 'heading', + }, + ], + sv: [ + { + content: 'Geojson test: Pasila', + type: 'heading', + }, + ], + }, + backgroundColor: '#ffffff', + textColor: '#449599', + type: 'info', + id: '16082019_120612_45', + persistence: 'repeat', + priority: '2', + shouldTrigger: true, + }, + ]; + + await triggerMessage(lon, lat, mockContext, msgs); + expect(store.getMessages()[0].shouldTrigger).to.equal(true); + }); +});