From 7b5d3e7430c0e8de997a522313ea68de9bbaa8cd Mon Sep 17 00:00:00 2001 From: Martin Donnelly Date: Sun, 25 Mar 2018 21:43:44 +0100 Subject: [PATCH] added nearby list and maps to venue cards --- package-lock.json | 5 ++ package.json | 1 + server.js | 14 ++-- server/directions.js | 1 + server/foursquare.js | 6 +- src/css/custom.scss | 6 ++ src/service-worker.js | 16 +++- src/v1/index.html | 3 + src/v1/js/Nearby.js | 50 +++++------- src/v1/js/NearbyList.js | 149 ++++++++++++++++++++++++++++++++++++ src/v1/js/RightByMe.js | 2 +- src/v1/js/Traffic.js | 17 ++++ src/v1/js/VenueDetail.js | 29 +++++++ src/v1/js/app.js | 5 +- src/v1/js/libs/fsbits.js | 34 ++++++++ src/v1/js/libs/templates.js | 5 +- 16 files changed, 297 insertions(+), 46 deletions(-) create mode 100644 src/v1/js/NearbyList.js create mode 100644 src/v1/js/libs/fsbits.js diff --git a/package-lock.json b/package-lock.json index ccda070..1dade87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6296,6 +6296,11 @@ "invert-kv": "1.0.0" } }, + "leaflet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.1.tgz", + "integrity": "sha512-adQOIzh+bfdridLM1xIgJ9VnJbAUY3wqs/ueF+ITla+PLQ1z47USdBKUf+iD9FuUA8RtlT6j6hZBfZoA6mW+XQ==" + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", diff --git a/package.json b/package.json index 6b2371b..9d0460b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "hh-mm-ss": "^1.2.0", "humanize-duration": "^3.13.0", "jquery": "^3.3.1", + "leaflet": "^1.3.1", "lodash": "^4.17.5", "log4js": "^2.5.3", "loggy": "^1.0.2", diff --git a/server.js b/server.js index 2d7c503..b33367e 100644 --- a/server.js +++ b/server.js @@ -77,14 +77,18 @@ app.get('/weatheralert', cache('15 minutes'), (req, res) => { }); app.get('/fsexplore', cache('15 minutes'), (req, res) => { - if (req.query.hasOwnProperty('ll')) - foursquare.doGetFourSquareExplore(req.query.ll) + if (req.query.hasOwnProperty('ll')) { + const ll = req.query.ll; + const limit = req.query.hasOwnProperty('ll') ? req.query.limit : 3; + const section = req.query.hasOwnProperty('section') ? req.query.section : 'topPicks'; + foursquare.doGetFourSquareExplore(ll, limit, section) .then((d) => { res.send(d); }).catch((e) => { logger.error(e); res.status(500).send('There was an error!'); }); + } else { // throw new Error('Weather: LL missing'); @@ -110,7 +114,7 @@ app.get('/rightbyme', cache('15 minutes'), (req, res) => { } }); -app.get('/nearbydetail'/*, cache('15 minutes')*/, (req, res) => { +app.get('/nearbydetail', cache('15 minutes'), (req, res) => { if (req.query.hasOwnProperty('id')) rightbyme.doGetMoreDetail(req.query.id) .then((d) => { @@ -173,7 +177,7 @@ app.get('/agenda', cache('15 minutes'), (req, res) => { app.get('/traffic', cache('5 minutes'), (req, res) => { logger.debug(req.query); - if (req.query.hasOwnProperty('olat')) { + if (req.query.hasOwnProperty('olat')) directions.getTraffic(req.query.olat, req.query.olon, req.query.dlat, req.query.dlon) .then((d) => { @@ -182,7 +186,7 @@ app.get('/traffic', cache('5 minutes'), (req, res) => { logger.error(e); res.status(500).send('There was an error!'); }); - } + else { // throw new Error('Weather: LL missing'); logger.warn('FS: oLat missing'); diff --git a/server/directions.js b/server/directions.js index 6b19ec2..d84eab5 100644 --- a/server/directions.js +++ b/server/directions.js @@ -25,6 +25,7 @@ function doGetEstDirections(olat, olon, dlat, dlon) { // Throw err; const output = reduceEstDirections(body); + output.fullBody = JSON.parse(body); output.timestamp = new Date().getTime(); console.log(output); diff --git a/server/foursquare.js b/server/foursquare.js index 3f0fa3f..842ad38 100644 --- a/server/foursquare.js +++ b/server/foursquare.js @@ -3,15 +3,15 @@ const foursquare = require('node-foursquare-venues')('IXXFUGW3NC3DEVS2V5EU4NV4CL logger.level = 'debug'; -function doGetFourSquareExplore(ll) { +function doGetFourSquareExplore(ll, limit = 3, section = 'topPicks') { const [lat, long ] = ll.split(','); return new Promise((resolve, reject) => { const fsObj = { 'll': ll, - 'section': 'topPicks', + 'section': section, 'v': '20170801', - 'limit': 3, + 'limit': limit, 'radius': 800 }; diff --git a/src/css/custom.scss b/src/css/custom.scss index 6890a6a..1127573 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -419,4 +419,10 @@ li { color: #fba010; } +#map { height: 180px; } + +#bymeImages { + margin-bottom: 3px; +} + @import "./src/css/weather"; diff --git a/src/service-worker.js b/src/service-worker.js index bf0a6a9..26683e5 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -const CACHE_VERSION = { 'version': '0.0.541' }; +const CACHE_VERSION = { 'version': '0.0.644' }; const dataCacheName = 'jubileeData-v1'; const cacheName = 'jubilee-final-1'; const filesToCache = [ @@ -51,7 +51,19 @@ const filesToCache = [ '/gfx/snow_d.jpg', '/gfx/snow_n.jpg', '/gfx/storm_d.jpg', - '/gfx/storm_n.jpg' + '/gfx/storm_n.jpg', + '/fonts/fonts.css', + '/fonts/fujicons.css', + '/fonts/fujicons.ttf', + '/fonts/Roboto-normal-100.woff', + '/fonts/Roboto-normal-300.woff', + '/fonts/Roboto-normal-400.woff', + '/fonts/Roboto-normal-500.woff', + '/fonts/Roboto-normal-600.woff', + '/fonts/Roboto-normal-900.woff', + '/fonts/Roboto_Condensed-normal-300.woff', + '/fonts/Roboto_Condensed-normal-400.woff' + ]; self.addEventListener('install', function(e) { diff --git a/src/v1/index.html b/src/v1/index.html index 3067ec0..7644653 100644 --- a/src/v1/index.html +++ b/src/v1/index.html @@ -10,6 +10,9 @@ + diff --git a/src/v1/js/Nearby.js b/src/v1/js/Nearby.js index 68fb729..f811c6c 100644 --- a/src/v1/js/Nearby.js +++ b/src/v1/js/Nearby.js @@ -8,39 +8,10 @@ const { toHour } = require('./libs/utils'); const TimeFormat = require('hh-mm-ss'); const { FSDetailView } = require('./Foursquare'); - -// console.notificationsTitle = 'Nearby'; - -const fsItem = Backbone.Model.extend({ - -}); - -const FSCollection = Backbone.Collection.extend({ - 'model': fsItem -}); +const { fsItem, FSCollection, FSItemView } = require('./libs/fsbits'); const fsCollection = new FSCollection(); -const fsItemView = Backbone.View.extend({ - 'tagName': 'div', - 'className': 'itemRow mui--align-middle', - 'template': _.template(` -<%= name %> <%= category %> `), - 'initialize': function() { - this.render(); - }, - 'attributes': function() { - return { - 'data-id': this.model.id - }; - }, - - 'render': function() { - console.log(this.model.attributes); - this.$el.html(this.template(this.model.toJSON())); - } -}); - const NearbyModel = Backbone.Model.extend({ 'defaults' : function (obj) { // return a new object @@ -128,6 +99,8 @@ const NearbyView = Backbone.View.extend({ this.location = options.location; this.fsCollection = fsCollection; + _.bindAll(this, 'doMoreClick'); + // this.model.bind('change', this.render, this); this.location.bind('change:llFixed', this.updateLocation, this); this.location.bind('change:atHome', this.atHome, this); @@ -140,7 +113,8 @@ const NearbyView = Backbone.View.extend({ this.other = $(''); }, 'events': { - 'click .itemRow': 'doClick' + 'click .itemRow': 'doClick', + 'click #nearbyOther': 'doMoreClick' }, 'updateLocation': function(l) { console.log('>> Nearby Location has changed...'); @@ -162,7 +136,7 @@ const NearbyView = Backbone.View.extend({ console.log('>> totalResults', totalResults); this.$el.empty(); this.fsCollection.each(function(item) { - const fsView = new fsItemView({ 'model': item }); + const fsView = new FSItemView({ 'model': item }); this.$el.append(fsView.el); // this.$el.append(personView.el); // adding all the person objects. }, this); @@ -183,6 +157,7 @@ const NearbyView = Backbone.View.extend({ console.log('>> Nearby received focus msg'); if (!this.model.has('time')) { console.log('No time yet'); + return ; } if (this.location.get('atHome')) { @@ -201,6 +176,17 @@ const NearbyView = Backbone.View.extend({ }, 'atHome': function() { if (this.location.get('atHome')) this.$el.parent().hide(); + }, + 'doMoreClick': function(d) { + console.log('Do more click', d); + + const llFixed = this.model.get('llFixed'); + const section = this.model.get('section'); + const data = { llFixed, section, 'limit':20 }; + + // console.log('Data', data); + + this.eventBus.trigger('showNearbyList', data); } }); diff --git a/src/v1/js/NearbyList.js b/src/v1/js/NearbyList.js new file mode 100644 index 0000000..948fdbf --- /dev/null +++ b/src/v1/js/NearbyList.js @@ -0,0 +1,149 @@ +const $ = require('jquery'); +const _ = require('underscore'); +const Backbone = require('backbone'); +const request = require('request'); +const { get } = require('lodash'); +const { reduceNearby } = require('./libs/reducers'); +const { createPanel, addPanel } = require('./libs/panel'); +const TimeFormat = require('hh-mm-ss'); + +const { FSCollection, FSItemView } = require('./libs/fsbits'); + +const fsCollection = new FSCollection(); + +const NearbyListModel = Backbone.Model.extend({ + 'defaults' : function (obj) { + // return a new object + return { + 'update' : new Date().getTime() + }; + }, 'initialize': function() { + this.fsCollection = fsCollection; + this.listenTo(this, 'change:update', this.onChange); + }, + 'onChange': function() { + this.getNearby(); + }, + 'getNearby': function() { + const llFixed = this.get('llFixed'); + + const hour = parseInt((new Date()).getHours().toString(), 10); + const section = this.get('section'); + const limit = this.get('limit'); + const time = new Date().getTime() ; + + const lastUpdate = time - (this.get('time') || 0); + + console.log('>> Nearby section:', hour, section); + console.info('>> Nearby:request'); + console.log(`>> Nearby last fetch: ${TimeFormat.fromMs(lastUpdate, 'hh:mm')} ago`); + + if (lastUpdate > 120000) + request({ + 'url': `${window.loc}/fsexplore`, + 'method': 'GET', + 'qs': { + 'll': llFixed, + 'section': section, + 'limit': limit + } + }, function(err, res, body) { + if (err) + console.error(err); + else { + console.log('statusCode', res.statusCode); + const fsJSON = JSON.parse(body); + const groups = get(fsJSON, 'response.groups'); + const items = groups[0].items; + const newItems = []; + this.set('totalResults', get(fsJSON, 'response.totalResults')); + for(const item of items) + newItems.push(reduceNearby(item)); + + this.fsCollection.reset(newItems); + this.logUpdate(); + } + }.bind(this)); + }, 'logUpdate': function() { + console.log('NearyList logging:'); + + const time = new Date().getTime() ; + + this.set('time', time); + + this.timerID = setTimeout( + () => this.tick(), + 3.6e+6 + 1000 + ); + }, + 'tick': function() { + console.log('Set update'); + this.set('update', new Date().getTime()); + } + +}); + +const NearbyListView = Backbone.View.extend({ + 'initialize': function(options) { + this.eventBus = options.eventBus; + + _.bindAll(this, 'doClick'); + + this.model.fsCollection.bind('reset', this.render, this); + this.eventBus.on('showNearbyList', this.showNearbyListPanel, this); + }, + 'showNearbyListPanel': function(data) { + console.log('Showing nearby list', data); + + const prevll = this.model.get('llFixed'); + const lastTime = this.model.get('last'); + const now = new Date().getTime(); + + this.model.set(data); + + this.model.set('update', new Date().getTime()); + this.$newPanel = createPanel({ 'title':'Nearby', 'divId':'NearbyListP' }); + + this.$el = addPanel(this.$newPanel); + + this.$el.empty(); + this.$newPanel.show(); + + // console.log(this.model); + if (prevll === data.llFixed) + if (now > lastTime + (60 * 1000 * 60)) { + this.model.set('update', now); + } + else { + this.render(); + } + }, 'events': { + 'click': 'doClick' + + }, 'doClick': function(d) { + console.log('Do click', d); + const id = get(d, 'currentTarget.dataset.id', ''); + console.log(id); + this.eventBus.trigger('showVenueDetail', id); + }, + 'render' : function() { + console.log('>> Do render'); + + console.info('>> Nearby:Render'); + const totalResults = this.model.get('totalResults'); + console.log('>> totalResults', totalResults); + this.$el.empty(); + this.model.fsCollection.each(function(item) { + const fsView = new FSItemView({ 'model': item }); + this.$el.append(fsView.el); + // this.$el.append(personView.el); // adding all the person objects. + }, this); + + this.$el.append(this.other); + + this.$el.find('.itemRow').on('click', this.doClick); + } + +}); + +module.exports = { NearbyListModel, NearbyListView }; diff --git a/src/v1/js/RightByMe.js b/src/v1/js/RightByMe.js index 8ba8fa3..4dec709 100644 --- a/src/v1/js/RightByMe.js +++ b/src/v1/js/RightByMe.js @@ -42,7 +42,7 @@ const ByMeModel = Backbone.Model.extend({ clearInterval(this.timerID); this.timerID = 0; - console.log(this.toJSON()); + // console.log(this.toJSON()); // const section = (partOfDay >= 11 && partOfDay <= 14) ? 'food' : 'topPicks'; request({ 'url': `${window.loc}/rightbyme`, diff --git a/src/v1/js/Traffic.js b/src/v1/js/Traffic.js index 8a54631..fa43525 100644 --- a/src/v1/js/Traffic.js +++ b/src/v1/js/Traffic.js @@ -174,4 +174,21 @@ const TrafficView = Backbone.View.extend({ }); +const parts = new Map( + [ + ['0', 'Depart'], + ['9', 'left_turn'], + ['13', 'right_turn'], + ['11', 'continue'], + ['23','right_fork'], + ['17','left_exit'], + ['19', 'left_ramp'], + ['29', 'turn'], + ['',''] + + ] + + +); + module.exports = { TrafficModel, TrafficView }; diff --git a/src/v1/js/VenueDetail.js b/src/v1/js/VenueDetail.js index 1f38fcf..b35b843 100644 --- a/src/v1/js/VenueDetail.js +++ b/src/v1/js/VenueDetail.js @@ -4,6 +4,7 @@ const Backbone = require('backbone'); const request = require('request'); const { get, isEmpty } = require('lodash'); const { createPanel, addPanel } = require('./libs/panel'); +const L = require('leaflet'); const templates = require('./libs/templates'); @@ -43,6 +44,7 @@ const VenueDetailModel = Backbone.Model.extend({ const VenueDetailView = Backbone.View.extend({ 'initialize': function(options) { this.eventBus = options.eventBus; + this.location = options.location; this.model.bind('change:details', this.doRender, this); this.eventBus.on('showVenueDetail', this.showNewsPanel, this); @@ -81,11 +83,16 @@ const VenueDetailView = Backbone.View.extend({ const m = this.model.get('details'); // this.imagesTemplate(this.model.toJSON()), this.yelpTemplate(this.model.toJSON()), this.tipsTemplate(this.model.toJSON()) + // console.log(this.model.attributes); contents.push(templates.venueTitle(m)); + const $map = $('#map'); + if (!isEmpty(m.images)) contents.push(templates.imagesTemplate(m)); + contents.push(templates.map()); + if (!isEmpty(m.yelp)) contents.push(templates.yelpTemplate(m)); @@ -97,7 +104,29 @@ const VenueDetailView = Backbone.View.extend({ contents.push(templates.openInFS(m)); this.$el.html(contents.join('')); + this.$el.append($map); + this.map = L.map('map', { + 'center': [m.latitude, m.longitude], + 'zoom': 15 + }); + + L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { + 'attribution': 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', + 'maxZoom': 18, + 'id': 'mapbox.streets', + 'accessToken': 'pk.eyJ1IjoibWFydGluZDIwMDAiLCJhIjoiY2pmNnlnc3F1MGpoYzJ5bXpscGFwaTlueiJ9.sx1ToptfsUf5HF3-0VVC-Q' + }).addTo(this.map); + + /* const marker =*/ L.marker([m.latitude, m.longitude]).addTo(this.map); + /* var circle =*/ L.circle(this.location.get('ll').split(','), { + 'color': 'blue', + 'fillColor': '#00a6ff', + 'fillOpacity': 0.5, + 'radius': 10 + }).addTo(this.map); + + // console.log(this.location.attributes); } }); diff --git a/src/v1/js/app.js b/src/v1/js/app.js index 2ec489a..9f0f641 100644 --- a/src/v1/js/app.js +++ b/src/v1/js/app.js @@ -17,6 +17,7 @@ const { ByMeModel, ByMeView } = require('./RightByMe'); const { VenueDetailModel, VenueDetailView } = require('./VenueDetail'); const { AgendaModel, AgendaView } = require('./Agenda'); const { TrafficModel, TrafficView } = require('./Traffic'); +const { NearbyListModel, NearbyListView } = require('./NearbyList'); var app = app || {}; const live = true; @@ -71,7 +72,9 @@ else app.traffic = new TrafficView({ 'model': new TrafficModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#traffic' }); - app.newsCard = new VenueDetailView({ 'model': new VenueDetailModel(), 'eventBus': app.eventBus }); + app.newsCard = new VenueDetailView({ 'model': new VenueDetailModel(), 'eventBus': app.eventBus, 'location': app.locationModel }); + + app.nearbyList = new NearbyListView({ 'model': new NearbyListModel(), 'eventBus' : app.eventBus }); app.updateOnlineStatus = function(event) { if (navigator.onLine) diff --git a/src/v1/js/libs/fsbits.js b/src/v1/js/libs/fsbits.js new file mode 100644 index 0000000..8cf89b2 --- /dev/null +++ b/src/v1/js/libs/fsbits.js @@ -0,0 +1,34 @@ +const Backbone = require('backbone'); +const _ = require('underscore'); + +const fsItem = Backbone.Model.extend({ + +}); + +const FSCollection = Backbone.Collection.extend({ + 'model': fsItem +}); + +// const fsCollection = new FSCollection(); + +const FSItemView = Backbone.View.extend({ + 'tagName': 'div', + 'className': 'itemRow mui--align-middle', + 'template': _.template(` +<%= name %> <%= category %> `), + 'initialize': function() { + this.render(); + }, + 'attributes': function() { + return { + 'data-id': this.model.id + }; + }, + + 'render': function() { + // console.log(this.model.attributes); + this.$el.html(this.template(this.model.toJSON())); + } +}); + +module.exports = { fsItem, FSCollection, FSItemView }; diff --git a/src/v1/js/libs/templates.js b/src/v1/js/libs/templates.js index 0092f0b..bbe24f8 100644 --- a/src/v1/js/libs/templates.js +++ b/src/v1/js/libs/templates.js @@ -59,9 +59,10 @@ const templates = { 'venueTitle' : _.template(`
<%=name %>
`), - 'openInFS' : _.template(` + 'openInFS' : _.template(`
Open in Foursquare
- `) + `), + 'map': _.template('
') }; module.exports = templates;