From 7e95b0a294d99360ab728fd9ba3c82a472d87c7d Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Wed, 22 Mar 2017 16:15:53 +0500 Subject: Implement extended device filtering --- web/app/Application.js | 4 +- web/app/Style.js | 2 +- web/app/controller/Root.js | 4 ++ web/app/store/DeviceStatuses.js | 35 ++++++++++ web/app/store/VisibleDevices.js | 22 ++++++ web/app/view/Devices.js | 136 ++++++++++++++---------------------- web/app/view/MapMarkerController.js | 67 ++++++++++++++++-- web/l10n/en.json | 5 ++ 8 files changed, 185 insertions(+), 90 deletions(-) create mode 100644 web/app/store/DeviceStatuses.js create mode 100644 web/app/store/VisibleDevices.js diff --git a/web/app/Application.js b/web/app/Application.js index f2baa0c5..9b450466 100644 --- a/web/app/Application.js +++ b/web/app/Application.js @@ -75,7 +75,9 @@ Ext.define('Traccar.Application', { 'DeviceImages', 'Calendars', 'AllCalendars', - 'AllTimezones' + 'AllTimezones', + 'VisibleDevices', + 'DeviceStatuses' ], controllers: [ diff --git a/web/app/Style.js b/web/app/Style.js index d7f01f29..c594d92e 100644 --- a/web/app/Style.js +++ b/web/app/Style.js @@ -34,7 +34,7 @@ Ext.define('Traccar.Style', { dateFormat: 'Y-m-d', weekStartDay: 1, - deviceWidth: 350, + deviceWidth: 400, reportHeight: 250, diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js index b858b20c..e8d4f0ff 100644 --- a/web/app/controller/Root.js +++ b/web/app/controller/Root.js @@ -25,6 +25,10 @@ Ext.define('Traccar.controller.Root', { 'Traccar.model.Position' ], + init: function () { + Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); + }, + onLaunch: function () { Ext.Ajax.request({ scope: this, diff --git a/web/app/store/DeviceStatuses.js b/web/app/store/DeviceStatuses.js new file mode 100644 index 00000000..14c1f950 --- /dev/null +++ b/web/app/store/DeviceStatuses.js @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +Ext.define('Traccar.store.DeviceStatuses', { + extend: 'Ext.data.Store', + fields: ['id', 'name', 'tdCls'], + + data: [{ + id: 'online', + name: Strings.deviceStatusOnline, + tdCls: 'view-color-green' + }, { + id: 'offline', + name: Strings.deviceStatusOffline, + tdCls: 'view-color-red' + }, { + id: 'unknown', + name: Strings.deviceStatusUnknown, + tdCls: 'view-color-yellow' + }] +}); diff --git a/web/app/store/VisibleDevices.js b/web/app/store/VisibleDevices.js new file mode 100644 index 00000000..db1ca7da --- /dev/null +++ b/web/app/store/VisibleDevices.js @@ -0,0 +1,22 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +Ext.define('Traccar.store.VisibleDevices', { + extend: 'Ext.data.ChainedStore', + source: 'Devices' +}); diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 645b8e7f..165fd032 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,20 +20,19 @@ Ext.define('Traccar.view.Devices', { xtype: 'devicesView', requires: [ + 'Ext.grid.filters.Filters', 'Traccar.view.DevicesController', 'Traccar.view.EditToolbar' ], controller: 'devices', - rootVisible: false, - initComponent: function () { - this.store = Ext.create('Ext.data.ChainedStore', { - source: 'Devices', - groupField: 'groupId' - }); - this.callParent(); - }, + plugins: 'gridfilters', + + store: 'VisibleDevices', + + stateful: true, + stateId: 'devices-grid', tbar: { componentCls: 'toolbar-header-style', @@ -85,79 +84,30 @@ Ext.define('Traccar.view.Devices', { }] }, - bbar: [{ - xtype: 'tbtext', - html: Strings.groupParent - }, { - xtype: 'combobox', - store: 'Groups', - queryMode: 'local', - displayField: 'name', - valueField: 'id', - flex: 1, - listeners: { - change: function () { - if (Ext.isNumber(this.getValue())) { - this.up('grid').store.filter({ - id: 'groupFilter', - filterFn: function (item) { - var groupId, group, groupStore, filter = true; - groupId = item.get('groupId'); - groupStore = Ext.getStore('Groups'); - - while (groupId) { - group = groupStore.getById(groupId); - if (group) { - if (group.get('id') === this.getValue()) { - filter = false; - break; - } - groupId = group.get('groupId'); - } else { - groupId = 0; - } - } - - return !filter; - }, - scope: this - }); - } else { - this.up('grid').store.removeFilter('groupFilter'); - } - } - } - }, { - xtype: 'tbtext', - html: Strings.sharedSearch - }, { - xtype: 'textfield', - flex: 1, - listeners: { - change: function () { - this.up('grid').store.filter('name', this.getValue()); - } - } - }], - listeners: { selectionchange: 'onSelectionChange' }, columns: { defaults: { - flex: 1, + flex: 2, minWidth: Traccar.Style.columnWidthNormal }, items: [{ text: Strings.sharedName, - dataIndex: 'name' + dataIndex: 'name', + filter: { + type: 'string', + itemDefaults: { + emptyText: Strings.deviceSearchFor + } + } }, { text: Strings.deviceIdentifier, dataIndex: 'uniqueId', hidden: true }, { - text: Strings.devicePhone, + text: Strings.sharedPhone, dataIndex: 'phone', hidden: true }, { @@ -168,27 +118,47 @@ Ext.define('Traccar.view.Devices', { text: Strings.deviceContact, dataIndex: 'contact', hidden: true + }, { + text: Strings.groupDialog, + dataIndex: 'groupId', + hidden: true, + filter: { + type: 'list', + labelField: 'name', + store: 'Groups' + }, + renderer: function (value) { + var group; + if (value !== 0) { + group = Ext.getStore('Groups').getById(value); + return group ? group.get('name') : value; + } + } }, { text: Strings.deviceLastUpdate, dataIndex: 'lastUpdate', - renderer: function (value, metaData, record) { - switch (record.get('status')) { - case 'online': - metaData.tdCls = 'view-color-green'; - break; - case 'offline': - metaData.tdCls = 'view-color-red'; - break; - default: - metaData.tdCls = 'view-color-yellow'; - break; - } - if (Traccar.app.getPreference('twelveHourFormat', false)) { - return Ext.Date.format(value, Traccar.Style.dateTimeFormat12); - } else { - return Ext.Date.format(value, Traccar.Style.dateTimeFormat24); + renderer: function (value) { + if (value) { + if (Traccar.app.getPreference('twelveHourFormat', false)) { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat12); + } else { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat24); + } } } + }, { + text: Strings.deviceStatus, + dataIndex: 'status', + flex: 1, + filter: { + type: 'list', + labelField: 'name', + store: 'DeviceStatuses' + }, + renderer: function (value, metaData) { + metaData.tdCls = Ext.getStore('DeviceStatuses').getById(value).get('tdCls'); + return Ext.getStore('DeviceStatuses').getById(value).get('name'); + } }] } }); diff --git a/web/app/view/MapMarkerController.js b/web/app/view/MapMarkerController.js index bb418704..bf3a2c0d 100644 --- a/web/app/view/MapMarkerController.js +++ b/web/app/view/MapMarkerController.js @@ -40,6 +40,12 @@ Ext.define('Traccar.view.MapMarkerController', { update: 'updateDevice', remove: 'removeDevice' }, + '#VisibleDevices': { + add: 'updateVisibleDevice', + update: 'updateVisibleDevice', + remove: 'updateVisibleDevice', + refresh: 'filterDevices' + }, '#LatestPositions': { add: 'updateLatest', update: 'updateLatest' @@ -198,11 +204,11 @@ Ext.define('Traccar.view.MapMarkerController', { } else { feature = new ol.Feature(new ol.geom.Circle(center, radius)); feature.setStyle(this.getAreaStyle(null, Traccar.Style.mapAccuracyColor)); - this.getView().getAccuracySource().addFeature(feature); + feature.setId(position.get('deviceId')); this.accuracyCircles[position.get('deviceId')] = feature; } } else { - if (feature) { + if (feature && this.getView().getAccuracySource().getFeatureById(feature.getId())) { this.getView().getAccuracySource().removeFeature(feature); } delete this.accuracyCircles[position.get('deviceId')]; @@ -232,9 +238,9 @@ Ext.define('Traccar.view.MapMarkerController', { device.get('category')); style.getText().setText(device.get('name')); marker.setStyle(style); + marker.setId(device.get('id')); this.latestMarkers[deviceId] = marker; - this.getView().getMarkersSource().addFeature(marker); - + this.checkDeviceVisibility(device); } if (marker === this.selectedMarker && this.lookupReference('deviceFollowButton').pressed) { @@ -268,8 +274,8 @@ Ext.define('Traccar.view.MapMarkerController', { ]) }); liveLine.setStyle(this.getRouteStyle(deviceId)); + liveLine.setId(position.get('deviceId')); this.liveRoutes[deviceId] = liveLine; - this.getView().getLiveRouteSource().addFeature(liveLine); } }, @@ -488,5 +494,56 @@ Ext.define('Traccar.view.MapMarkerController', { } else if (point) { this.getView().getMapView().fit(new ol.geom.Point(point)); } + }, + + updateVisibleDevice: function (store, data) { + var i, device; + + if (!Ext.isArray(data)) { + data = [data]; + } + + for (i = 0; i < data.length; i++) { + device = data[i]; + if (device.get('id') in this.latestMarkers) { + this.checkDeviceVisibility(device); + } + } + }, + + checkDeviceVisibility: function (device) { + var deviceId, accuracy, liveLine, marker; + deviceId = device.get('id'); + marker = this.latestMarkers[deviceId]; + accuracy = this.accuracyCircles[deviceId]; + liveLine = this.liveRoutes[deviceId]; + if (Ext.getStore('VisibleDevices').contains(device)) { + if (marker && !this.getView().getMarkersSource().getFeatureById(marker.getId())) { + this.getView().getMarkersSource().addFeature(marker); + } + if (accuracy && !this.getView().getAccuracySource().getFeatureById(accuracy.getId())) { + this.getView().getAccuracySource().addFeature(accuracy); + } + if (liveLine && !this.getView().getLiveRouteSource().getFeatureById(liveLine.getId())) { + this.getView().getLiveRouteSource().addFeature(liveLine); + } + } else { + if (marker && this.getView().getMarkersSource().getFeatureById(marker.getId())) { + this.getView().getMarkersSource().removeFeature(marker); + } + if (this.selectedMarker && marker && marker.getId() === this.selectedMarker.getId()) { + this.deselectFeature(); + } + if (accuracy && this.getView().getAccuracySource().getFeatureById(accuracy.getId())) { + this.getView().getAccuracySource().removeFeature(accuracy); + } + if (liveLine && this.getView().getLiveRouteSource().getFeatureById(liveLine.getId())) { + this.getView().getLiveRouteSource().removeFeature(liveLine); + } + } + }, + + filterDevices: function (store) { + Ext.getStore('Devices').each(this.checkDeviceVisibility, this, false); } }); diff --git a/web/l10n/en.json b/web/l10n/en.json index 73ea0504..74f4ae4c 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -84,6 +84,11 @@ "deviceCommand": "Command", "deviceFollow": "Follow", "deviceTotalDistance": "Total Distance", + "deviceStatus": "Status", + "deviceSearchFor": "Search for...", + "deviceStatusOnline": "Online", + "deviceStatusOffline": "Offline", + "deviceStatusUnknown": "Unknown", "groupDialog": "Group", "groupParent": "Group", "groupNoGroup": "No Group", -- cgit v1.2.3 From 49f6c1456371dd0bc066a9d6ad40bef824c39977 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Wed, 22 Mar 2017 17:20:20 +0500 Subject: Fix Status column renderer --- web/app/view/Devices.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 165fd032..84ed800d 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -156,8 +156,10 @@ Ext.define('Traccar.view.Devices', { store: 'DeviceStatuses' }, renderer: function (value, metaData) { - metaData.tdCls = Ext.getStore('DeviceStatuses').getById(value).get('tdCls'); - return Ext.getStore('DeviceStatuses').getById(value).get('name'); + if (value) { + metaData.tdCls = Ext.getStore('DeviceStatuses').getById(value).get('tdCls'); + return Ext.getStore('DeviceStatuses').getById(value).get('name'); + } } }] } -- cgit v1.2.3 From e5cda1b3e0a4735abd512652b5df30331ad6782e Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Thu, 23 Mar 2017 14:37:23 +0500 Subject: - Remove search string - Rename functions and variables - Fix device deselect - Fix appearing features and double visibility check --- web/app/model/Device.js | 15 +++++++---- web/app/store/DeviceStatuses.js | 8 +++--- web/app/view/Devices.js | 15 ++++++----- web/app/view/MapMarkerController.js | 50 ++++++++++++++++++++++++------------- web/l10n/en.json | 1 - 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/web/app/model/Device.js b/web/app/model/Device.js index f733a303..9de5828d 100644 --- a/web/app/model/Device.js +++ b/web/app/model/Device.js @@ -30,19 +30,24 @@ Ext.define('Traccar.model.Device', { type: 'string' }, { name: 'phone', - type: 'string' + type: 'string', + allowNull: true }, { name: 'model', - type: 'string' + type: 'string', + allowNull: true }, { name: 'contact', - type: 'string' + type: 'string', + allowNull: true }, { name: 'category', - type: 'string' + type: 'string', + allowNull: true }, { name: 'status', - type: 'string' + type: 'string', + allowNull: true }, { name: 'lastUpdate', type: 'date', diff --git a/web/app/store/DeviceStatuses.js b/web/app/store/DeviceStatuses.js index 14c1f950..eafba241 100644 --- a/web/app/store/DeviceStatuses.js +++ b/web/app/store/DeviceStatuses.js @@ -17,19 +17,19 @@ */ Ext.define('Traccar.store.DeviceStatuses', { extend: 'Ext.data.Store', - fields: ['id', 'name', 'tdCls'], + fields: ['id', 'name', 'color'], data: [{ id: 'online', name: Strings.deviceStatusOnline, - tdCls: 'view-color-green' + color: 'view-color-green' }, { id: 'offline', name: Strings.deviceStatusOffline, - tdCls: 'view-color-red' + color: 'view-color-red' }, { id: 'unknown', name: Strings.deviceStatusUnknown, - tdCls: 'view-color-yellow' + color: 'view-color-yellow' }] }); diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 84ed800d..eee214ee 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -96,12 +96,7 @@ Ext.define('Traccar.view.Devices', { items: [{ text: Strings.sharedName, dataIndex: 'name', - filter: { - type: 'string', - itemDefaults: { - emptyText: Strings.deviceSearchFor - } - } + filter: 'string' }, { text: Strings.deviceIdentifier, dataIndex: 'uniqueId', @@ -156,9 +151,13 @@ Ext.define('Traccar.view.Devices', { store: 'DeviceStatuses' }, renderer: function (value, metaData) { + var status; if (value) { - metaData.tdCls = Ext.getStore('DeviceStatuses').getById(value).get('tdCls'); - return Ext.getStore('DeviceStatuses').getById(value).get('name'); + status = Ext.getStore('DeviceStatuses').getById(value); + if (status) { + metaData.tdCls = status.get('color'); + return status.get('name'); + } } } }] diff --git a/web/app/view/MapMarkerController.js b/web/app/view/MapMarkerController.js index bf3a2c0d..fa380e76 100644 --- a/web/app/view/MapMarkerController.js +++ b/web/app/view/MapMarkerController.js @@ -32,6 +32,9 @@ Ext.define('Traccar.view.MapMarkerController', { '*': { selectdevice: 'selectDevice', selectreport: 'selectReport' + }, + 'devices': { + deselectfeature: 'deselectDevice' } }, store: { @@ -41,9 +44,9 @@ Ext.define('Traccar.view.MapMarkerController', { remove: 'removeDevice' }, '#VisibleDevices': { - add: 'updateVisibleDevice', - update: 'updateVisibleDevice', - remove: 'updateVisibleDevice', + add: 'updateVisibleDevices', + update: 'updateVisibleDevices', + remove: 'updateVisibleDevices', refresh: 'filterDevices' }, '#LatestPositions': { @@ -180,14 +183,14 @@ Ext.define('Traccar.view.MapMarkerController', { device = Ext.getStore('Devices').getById(position.get('deviceId')); if (device) { - this.updateAccuracy(position); + this.updateAccuracy(position, device); this.updateLatestMarker(position, device); - this.updateLiveRoute(position); + this.updateLiveRoute(position, device); } } }, - updateAccuracy: function (position) { + updateAccuracy: function (position, device) { var center, radius, feature, mapView, projection, pointResolution; mapView = this.getView().getMapView(); feature = this.accuracyCircles[position.get('deviceId')]; @@ -206,6 +209,9 @@ Ext.define('Traccar.view.MapMarkerController', { feature.setStyle(this.getAreaStyle(null, Traccar.Style.mapAccuracyColor)); feature.setId(position.get('deviceId')); this.accuracyCircles[position.get('deviceId')] = feature; + if (this.isDeviceVisible(device)) { + this.getView().getAccuracySource().addFeature(feature); + } } } else { if (feature && this.getView().getAccuracySource().getFeatureById(feature.getId())) { @@ -240,7 +246,9 @@ Ext.define('Traccar.view.MapMarkerController', { marker.setStyle(style); marker.setId(device.get('id')); this.latestMarkers[deviceId] = marker; - this.checkDeviceVisibility(device); + if (this.isDeviceVisible(device)) { + this.getView().getMarkersSource().addFeature(marker); + } } if (marker === this.selectedMarker && this.lookupReference('deviceFollowButton').pressed) { @@ -248,7 +256,7 @@ Ext.define('Traccar.view.MapMarkerController', { } }, - updateLiveRoute: function (position) { + updateLiveRoute: function (position, device) { var deviceId, liveLine, liveCoordinates, lastLiveCoordinates, newCoordinates; deviceId = position.get('deviceId'); if (deviceId in this.liveRoutes) { @@ -276,6 +284,9 @@ Ext.define('Traccar.view.MapMarkerController', { liveLine.setStyle(this.getRouteStyle(deviceId)); liveLine.setId(position.get('deviceId')); this.liveRoutes[deviceId] = liveLine; + if (this.isDeviceVisible(device)) { + this.getView().getMarkersSource().addFeature(liveLine); + } } }, @@ -468,10 +479,14 @@ Ext.define('Traccar.view.MapMarkerController', { }, deselectFeature: function () { - this.selectMarker(null, false); + this.deselectDevice(); this.fireEvent('deselectfeature'); }, + deselectDevice: function () { + this.selectMarker(null, false); + }, + zoomToAllPositions: function (data) { var i, point, minx, miny, maxx, maxy; for (i = 0; i < data.length; i++) { @@ -496,7 +511,7 @@ Ext.define('Traccar.view.MapMarkerController', { } }, - updateVisibleDevice: function (store, data) { + updateVisibleDevices: function (store, data) { var i, device; if (!Ext.isArray(data)) { @@ -506,18 +521,22 @@ Ext.define('Traccar.view.MapMarkerController', { for (i = 0; i < data.length; i++) { device = data[i]; if (device.get('id') in this.latestMarkers) { - this.checkDeviceVisibility(device); + this.updateDeviceVisibility(device); } } }, - checkDeviceVisibility: function (device) { + isDeviceVisible: function (device) { + return Ext.getStore('VisibleDevices').contains(device); + }, + + updateDeviceVisibility: function (device) { var deviceId, accuracy, liveLine, marker; deviceId = device.get('id'); marker = this.latestMarkers[deviceId]; accuracy = this.accuracyCircles[deviceId]; liveLine = this.liveRoutes[deviceId]; - if (Ext.getStore('VisibleDevices').contains(device)) { + if (this.isDeviceVisible(device)) { if (marker && !this.getView().getMarkersSource().getFeatureById(marker.getId())) { this.getView().getMarkersSource().addFeature(marker); } @@ -531,9 +550,6 @@ Ext.define('Traccar.view.MapMarkerController', { if (marker && this.getView().getMarkersSource().getFeatureById(marker.getId())) { this.getView().getMarkersSource().removeFeature(marker); } - if (this.selectedMarker && marker && marker.getId() === this.selectedMarker.getId()) { - this.deselectFeature(); - } if (accuracy && this.getView().getAccuracySource().getFeatureById(accuracy.getId())) { this.getView().getAccuracySource().removeFeature(accuracy); } @@ -544,6 +560,6 @@ Ext.define('Traccar.view.MapMarkerController', { }, filterDevices: function (store) { - Ext.getStore('Devices').each(this.checkDeviceVisibility, this, false); + Ext.getStore('Devices').each(this.updateDeviceVisibility, this, false); } }); diff --git a/web/l10n/en.json b/web/l10n/en.json index 74f4ae4c..4b06735c 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -85,7 +85,6 @@ "deviceFollow": "Follow", "deviceTotalDistance": "Total Distance", "deviceStatus": "Status", - "deviceSearchFor": "Search for...", "deviceStatusOnline": "Online", "deviceStatusOffline": "Offline", "deviceStatusUnknown": "Unknown", -- cgit v1.2.3