UNPKG

atriusmaps-node-sdk

Version:

This project provides an API to Atrius Personal Wayfinder maps within a Node environment. See the README.md for more information

249 lines (223 loc) 9.06 kB
'use strict'; var R = require('ramda'); var Zousan = require('zousan'); var configUtils = require('../../../src/utils/configUtils.js'); var rand = require('../../../src/utils/rand.js'); var poiSearch = require('./poiSearch.js'); var searchTypeahead = require('./searchTypeahead.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var R__namespace = /*#__PURE__*/_interopNamespaceDefault(R); function create (app, config) { const state = { poiSearch: null, typeahead: null, indexesCreated: new Zousan(), // re-initialize this when changing venues defaultSearchTerms: null, specialQueryTerms: {} }; const init = async () => { const pois = await app.bus.get('poi/getAll'); state.poiSearch = poiSearch(pois, app.i18n().language); state.typeahead = searchTypeahead(pois, state.poiSearch.search, app.i18n().language); state.defaultSearchTerms = configUtils.getLocalized(config, 'defaultSearchTerms', app.i18n().language); state.indexesCreated.resolve(); }; /** * Prepares and send an event with up to 50 non-portal POIs sorted by distance to user location. */ // todo check if async events (events which send a new event as a result) are still needed app.bus.on('search/queryNearby', async () => { const pois = await searchNearby(); app.bus.send('search/showNearby', { pois, term: 'Nearby' }); return pois }); /** * Returns max 50 non-portal POIs sorted by distance to user location. * @returns Array.<POI> */ app.bus.on('search/queryNearbyAsync', searchNearby); async function searchNearby () { const startLocation = await app.bus.getFirst('user/getPhysicalLocation'); if (!startLocation?.floorId) return [] const poisSameFloor = await app.bus.get('poi/getByFloorId', { floorId: startLocation?.floorId }); const isNotPortal = poi => poi.category.indexOf('portal') === -1 && poi.category !== 'element.door'; const noPortalPois = Object.values(R__namespace.pickBy(isNotPortal, poisSameFloor)); const poisWithDistance = await app.bus.get('wayfinder/addPathTimeMultiple', { pois: noPortalPois, startLocation }); return R__namespace.sortBy(R__namespace.prop('distance'), Object.values(poisWithDistance)).slice(0, 50) } /** * Searches for POIs with search term or category name * and sends a new event 'search/showCategory' with search result POIs. * * @param {string} searchTerm - term to search POIs * @param {string} category - category name, fallback term to search POIs * @param {string} categoryName - label to display in search results input view */ app.bus.on('search/queryCategory', async ({ category, categoryName, searchTerm }) => { const pois = await state.indexesCreated.then(() => state.poiSearch.search({ query: searchTerm || category })); app.bus.send('search/showCategory', { pois, category, categoryName }); return pois }); /** * Searches for POIs with search term * and sends a new event 'search/showCategory' with search result POIs. * * @param {string} term - search term */ // todo introduce consistent naming for parameters are return values (searchTem or term, pois or results, keywords or searchTerms) app.bus.on('search/query', ({ term }) => { return state.indexesCreated.then(() => { const pois = state.poiSearch.search({ query: term }); app.bus.send('search/showSearchResults', { results: pois, term }); return pois }) }); /** * Search for POIs with search term. * * @param {string} term - search term * @returns Array.<POI> */ app.bus.on('search/queryAsync', ({ term }) => state.indexesCreated.then(() => state.poiSearch.search({ query: term })) ); /** * If search term matches any registered special query term, * then send an event with params for this special query, * otherwise search for POIs with term and send the event 'search/query' with search result POIs * * @param {string} term - search term or special query */ app.bus.on('search/queryWithSpecial', ({ term }) => { if (state.specialQueryTerms[term]) { const { event, params } = state.specialQueryTerms[term]; // use "send" as we can't gaurentee this event is a "get" or even returns POIs return app.bus.send(event, params) } else { return app.bus.get('search/query', { term }) } }); /** * Returns list of all localized default search terms declared in the configuration * or list of up to 'limit' random unique POI categories as a fallback. * Returns a list of keywords and sends an event 'search/showDefaultSearchKeywords' with same the list of keywords. * * @param {number} [limit=5] - Used to limit number of random unique categories list if default search terms are not defined in configuration. * @returns Array.<string> - list of keywords */ app.bus.on('search/getDefaultSearchTerms', async ({ limit = 5 } = {}) => { const defaultSearchTerms = state.defaultSearchTerms; const hasDefaultSearchTerms = defaultSearchTerms && defaultSearchTerms.length; const keywords = hasDefaultSearchTerms ? defaultSearchTerms : await getUniqueRandomCategories(limit); app.bus.send('search/showDefaultSearchKeywords', { keywords }); return keywords }); async function getUniqueRandomCategories (limit) { const allCategories = (await app.bus.send('poi/getAllCategories'))[0]; const uniqueCategories = Array.from(new Set(allCategories)); const shuffledUniqueCategories = rand.randomizeArray(uniqueCategories); return shuffledUniqueCategories.slice(0, limit) } /** * Returns list of up to 'limit' unique random navigable POIs. * * @param {number} [limit=5] * @returns Array.<POI> */ app.bus.on('search/getDefaultSearchPois', async ({ limit = 5 } = {}) => { const allPois = await app.bus.get('poi/getAll'); const navigablePred = (val, key) => val.isNavigable; const navigablePois = R__namespace.pickBy(navigablePred, allPois); return rand.arrayPick(Object.values(navigablePois), limit) }); /** * Registers an event and event params for special query term. * Optionally adds passed special query term to keywords search index. * * @param {string} term - special query term * @param {string} event - name of associated event * @param {Object} params - parameters for associated event * @param {boolean} addKeyword - indicates if term has to be added to keywords search index */ app.bus.on('search/registerSpecialQuery', ({ term, event, params, addKeyword = true }) => { state.indexesCreated.then(() => { if (addKeyword) { state.typeahead.addKeyword(term); } state.specialQueryTerms[term] = { event, params }; }); }); /** * Adds list of keywords to keywords search index. * * @param {Array.<string>} keywords - list of new keywords */ app.bus.on('search/addKeywords', ({ keywords }) => state.indexesCreated.then(() => keywords.forEach(keyword => state.typeahead.addKeyword(keyword)))); /** * Returns lists of keywords and POIs matching search term. * * For term shorter than 3 characters only keywords index is queried. * If no keywords are found or when term is longer, * then POIs index is queried for the the remaining results. * * @param {string} term - search term * @param {number} limit - maximum number of results * @returns {{ pois: Array.<POI>, keywords: Array.<String>, term: string }} - list of keywords, list of POIs, search term */ app.bus.on('search/typeahead', ({ term, limit }) => state.indexesCreated.then(() => { const { keywords, pois } = state.typeahead.query(term, limit); return { keywords, pois, term } })); /** * Resets plugin state. */ app.bus.on('venueData/loadNewVenue', () => { state.indexesCreated = new Zousan(); init(); }); /** * Updates POIs search index with dynamic Grab POIs. * * @param {string} plugin - type of dynamic data * @param {Object<string, Object>} - dictionary of POI id to dynamic data object */ app.bus.on('poi/setDynamicData', async ({ plugin, idValuesMap }) => { if (plugin !== 'grab') return const dynamicPoisPromises = Object.keys(idValuesMap) .map(id => app.bus.get('poi/getById', { id })); return Promise.all(dynamicPoisPromises) .then(pois => state.indexesCreated.then(() => state.poiSearch.updateMultiple(pois))) }); const runTest = async (initialState, testRoutine) => { // state = { ...initialState } await testRoutine(); return state }; return { init, runTest } } exports.create = create;