diff --git a/bower.json b/bower.json index 58a7c71974164a8af8f53242cf42e0e5152569de..91cbf42c9215f05ed45ef6242ebafe34ae862d0c 100644 --- a/bower.json +++ b/bower.json @@ -20,7 +20,8 @@ "ui-leaflet": "^2.0.0", "leaflet-search": "^2.7.2", "angular-leaflet-directive": "angular-leaflet#^0.10.0", - "Leaflet.EasyButton": "~1.3.2" + "Leaflet.EasyButton": "~1.3.2", + "leaflet.loading": "Leaflet.loading#^0.1.24" }, "resolutions": { "angular-sanitize": "1.5.3", diff --git a/www/index.html b/www/index.html index bf26b77bba58d438856d193833f54f4aef4adec6..ab4d88847eb4c82deb0eda8ea8576c421b6789a4 100644 --- a/www/index.html +++ b/www/index.html @@ -25,6 +25,7 @@ <!--endRemoveIf(no-device)--> <!--removeIf(device)--> <link rel="stylesheet" type="text/css" href="lib/leaflet-search/dist/leaflet-search.src.css"> + <link rel="stylesheet" type="text/css" href="lib/leaflet.loading/src/Control.Loading.css"> <!--endRemoveIf(device)--> <link rel="stylesheet" href="lib/Leaflet.EasyButton/src/easy-button.css"> <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/es/css/style.css"> @@ -89,6 +90,7 @@ <script src="lib/Leaflet.awesome-markers/dist/leaflet.awesome-markers.js"></script> <script src="lib/leaflet-search/dist/leaflet-search.src.js"></script> <script src="lib/Leaflet.EasyButton/src/easy-button.js"></script> + <script src="lib/leaflet.loading/src/Control.Loading.js"></script> <!--endRemoveIf(no-plugin)--> <!-- endbuild --> diff --git a/www/js/controllers/blockchain-controllers.js b/www/js/controllers/blockchain-controllers.js index ca77f5d3df00177ba38fd422cd77e7c542449af1..1296714c12fba4966ef0e75e503231b8477831f9 100644 --- a/www/js/controllers/blockchain-controllers.js +++ b/www/js/controllers/blockchain-controllers.js @@ -106,6 +106,7 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho $scope.searchTextId = null; $scope.ionItemClass = 'item-border-large'; $scope.defaultSizeLimit = UIUtils.screen.isSmall() ? 50 : 100; + $scope.helptipPrefix = 'helptip-network'; /** * Enter into the view diff --git a/www/plugins/es/js/services/geo-services.js b/www/plugins/es/js/services/geo-services.js index 87314b32216cf923a01ad177dfadeb71fc223ea2..86e67f8f0622080d3b6bc4ab6dd0482e8a4433b8 100644 --- a/www/plugins/es/js/services/geo-services.js +++ b/www/plugins/es/js/services/geo-services.js @@ -56,12 +56,12 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser function searchPositionByIP(ip) { - var now = new Date(); - console.debug('[ES] [geo] Searching IP position [{0}]...'.format(ip)); + //var now = new Date(); + //console.debug('[ES] [geo] Searching IP position [{0}]...'.format(ip)); return that.raw.searchByIP({ip: ip}) .then(function(res) { - console.debug('[ES] [geo] Found IP {0} position in {0}ms'.format(res ? 1 : 0, new Date().getTime() - now.getTime())); + //console.debug('[ES] [geo] Found IP {0} position in {0}ms'.format(res ? 1 : 0, new Date().getTime() - now.getTime())); return res ? {lat: res.latitude,lng: res.longitude} : undefined; }); } diff --git a/www/plugins/map/css/style.css b/www/plugins/map/css/style.css index f7cf16a0af44d85396b3d2e9f15d73814f13c042..159945a2d9b7b2ed6336a8563778c95ccff72080 100644 --- a/www/plugins/map/css/style.css +++ b/www/plugins/map/css/style.css @@ -56,5 +56,32 @@ } .leaflet-popup .item-peer { - overflow: visible; + padding-top: 0px; +} + +.view-map-network .leaflet-popup-content{ + max-height: 250px +} +.view-map-network .leaflet-popup-content .item-peer .row { + display: block; +} +.view-map-network .leaflet-popup-content .item-peer .col.col-15 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 15%; + -moz-box-flex: 0; + -moz-flex: 0 0 15%; + -ms-flex: 0 0 15%; + flex: 0 0 15%; + max-width: 70px; + text-align: start !important; +} +.view-map-network .leaflet-popup-content .item-peer .col.col-20 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -moz-box-flex: 0; + -moz-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 70px; + text-align: start !important; } diff --git a/www/plugins/map/js/controllers/network-controllers.js b/www/plugins/map/js/controllers/network-controllers.js index 3ec8deed8dfeadd20decd475e2f0cecb7a5fdfab..579ba2c1d197dd307539b6af252e17404388111b 100644 --- a/www/plugins/map/js/controllers/network-controllers.js +++ b/www/plugins/map/js/controllers/network-controllers.js @@ -44,29 +44,39 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map var formatPubkey = $filter('formatPubkey'), - markerTemplate = $templateCache.get('plugins/map/templates/network/popup_marker.html'), + markerMessageTemplate, // Create a hidden layer, to hold search markers searchLayer = L.layerGroup({visible: false}), + loadingControl, icons= { member: { type: 'awesomeMarker', icon: 'person', - markerColor: 'blue' + markerColor: 'green' }, mirror: { type: 'awesomeMarker', icon: 'android-desktop', - markerColor: 'lightgray' + markerColor: 'green' }, offline: { type: 'awesomeMarker', icon: 'ion-close-circled', markerColor: 'red' } - } + }, + markerIdByPeerId = {}, + markerCounter = 0 ; + // Init the template for marker popup + markerMessageTemplate = '<div class="item item-peer item-icon-left no-border" ng-click="selectPeer(peer)">'; + markerMessageTemplate += $templateCache.get('templates/network/item_content_peer.html'); + markerMessageTemplate += '</div>'; + markerMessageTemplate = markerMessageTemplate.replace(/[:]rebind[:]|[:][:]/g, ''); // remove binding limitation + $scope.mapId = 'map-network-' + $scope.$id; + $scope.helptipPrefix = 'helptip-' + $scope.mapId; // make to override, to avoid error during help tour $scope.map = MapUtils.map({ layers: { @@ -87,14 +97,14 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map visible: false } } - } + }, + markers: {} }); var inheritedEnter = $scope.enter; $scope.enter = function(e, state) { if ($scope.networkStarted) return; - console.log("[map] [network] Opening the view..."); // remember state, to be able to refresh location $scope.stateName = state && state.stateName; $scope.stateParams = angular.copy(state && state.stateParams||{}); @@ -152,95 +162,99 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map if (!$scope.search.results || !$scope.search.results.length) return; // nothing + var markerIdByPeerIdToRemove = angular.copy(markerIdByPeerId); + var lngStep = 0.001; + + var updateMarker = function(marker, peer) { + marker.layer = !peer.online ? 'offline' : (peer.uid ? 'member' : 'mirror'); + marker.icon = angular.copy(icons[marker.layer]); + marker.opacity = peer.online ? 0.9 : 0.7; + marker.title = peer.dns || peer.server; + if (peer.online && !peer.hasMainConsensusBlock) { + marker.icon.markerColor = peer.hasConsensusBlock ? 'beige' : 'lightgray'; + } + if (!marker.lng) { + marker.lng = marker.position.lng + Math.random() / 1000; + marker.lat = marker.position.lat + Math.random() / 1000; + } + + return marker; + }; - $q.all(data.peers.reduce(function(res, peer){ + _.forEach(data.peers, function(peer){ // skip TOR peer - if (peer.isTor()) return res; // already define - if (peer.lat && peer.lng) return res.concat(peer); // already define + if (peer.isTor()) return; // already define + // get marker id + var markerId = markerIdByPeerId[peer.id]; + + // if already exists + if (markerId && $scope.map.markers[markerId]) { + updateMarker($scope.map.markers[markerId], peer); + delete markerIdByPeerIdToRemove[peer.id]; + return; + } + + // Get position by IP var ip = peer.getHost(); - return res.concat(esGeo.point.searchByIP(ip) + esGeo.point.searchByIP(ip) + + // Create the marker .then(function(position){ - return angular.merge(peer, position); + var marker = updateMarker({ + position: position, + getMessageScope: function() { + var scope = $scope.$new(); + scope.peer = peer; + return scope; + }, + draggable: true, + focus: false, + message: markerMessageTemplate + }, peer); + + // Add marker to list + markerId = '' + markerCounter++; + $scope.map.markers[markerId] = marker; + markerIdByPeerId[peer.id] = markerId; }) .catch(function(err) { console.debug('No position found for IP ['+ip+']', err); - return; - })); - }, [])) - .then(function(res) { - var counter = 0; - //var markers = {}; - var markers = []; - _.forEach(res, function(peer) { - if (peer && peer.lat && peer.lng) { - var type = peer.uid ? 'member' : 'mirror'; - var marker = { - layer: type, - icon: icons[type], - opacity: peer.uid ? 0.9 : 0.6, - title: peer.dns || peer.server, - lat: peer.lat, - lng: peer.lng, - draggable: true, - getMessageScope: function() { - var scope = $scope.$new(); - scope.peer = peer; - return scope; - }, - focus: false, - message: markerTemplate - }; - //markers[''+counter++] = marker; - markers.push(marker); - } }); + }); - leafletData.getMap($scope.mapId) - .then(function(map) { - - $scope.map.markers = markers; - - // Update map center (if need) - var needCenterUpdate = $scope.stateCenter && !angular.equals($scope.map.center, $scope.stateCenter); - if (needCenterUpdate) { - MapUtils.updateCenter(map, $scope.stateCenter); - delete $scope.stateCenter; - } - - // Add localize me control - /*MapUtils.control.localizeMe().addTo(map); - - // Add search control - var searchTip = $interpolate($templateCache.get('plugins/map/templates/network/item_search_tooltip.html')); - MapUtils.control.search({ - layer: markersSearchLayer, - propertyName: 'title', - buildTip: function(text, val) { - return searchTip(val.layer.options); - } - }).addTo(map);*/ - }); - }) - ; - - // Create a search marker (will be hide) - /* var searchText = hit.title + ((hit.uid && hit.uid != hit.title) ? (' | ' + hit.uid) : '') + ' | ' + shortPubkey; - var searchMarker = angular.merge({ - type: type, - opacity: 0, - icon: L.divIcon({ - className: type + ' ng-hide', - iconSize: L.point(0, 0) - }) - }, {title: searchText, pubkey: hit.pubkey, uid: hit.uid, name: hit.title}); - markersSearchLayer.addLayer(new L.Marker({ - lat: hit.geoPoint.lat, - lng: hit.geoPoint.lon - }, - searchMarker));*/ - //console.log($scope.map.markers); + // Remove old markers not found in the new result + _.forEach(_.keys(markerIdByPeerIdToRemove), function(peerId) { + delete markerIdByPeerId[peerId]; + }); + _.forEach(_.values(markerIdByPeerIdToRemove), function(markerId) { + delete $scope.map.markers[markerId]; + }); + leafletData.getMap($scope.mapId).then(function(map) { + // Add loading control + if (!loadingControl) { + loadingControl = L.Control.loading({ + separate: true + }).addTo(map); + if ($scope.search.loading) { + map.fire('dataloading'); + } + } + + else if (!$scope.search.loading) { + $timeout(function() { + map.fire('dataload'); + }, 1000); + } + + // Recenter map// Update map center (if need) + var needCenterUpdate = $scope.stateCenter && !angular.equals($scope.map.center, $scope.stateCenter); + if (needCenterUpdate) { + MapUtils.updateCenter(map, $scope.stateCenter); + delete $scope.stateCenter; + } + }); }; }); diff --git a/www/plugins/map/templates/network/popup_marker.html b/www/plugins/map/templates/network/popup_marker.html deleted file mode 100644 index 6379f2f2cdc6a0c6fb09901f320fc62f1af3e92e..0000000000000000000000000000000000000000 --- a/www/plugins/map/templates/network/popup_marker.html +++ /dev/null @@ -1,33 +0,0 @@ -<div class="item no-border item-peer item-icon-left no-padding-top"> - - <i class="icon ion-android-desktop" - ng-class="{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" - ng-if="!peer.uid || !peer.avatar"></i> - <b class="icon-secondary ion-person" ng-if="!peer.avatar && peer.uid" - ng-class="{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" - style="left: 26px; top: -3px;"></b> - <i class="avatar" ng-if="peer.uid && peer.avatar" style="background-image: url('{{peer.avatar.src}}')"></i> - <b class="icon-secondary assertive ion-close-circled" ng-if="!peer.online" style="left: 37px; top: -10px;"></b> - - - <h2 class="dark"><a ng-click="selectPeer(peer)">{{peer.dns||peer.server}}</a></h2> - <h5 class="gray" ng-if="peer.dns && peer.dns != peer.server">{{peer.server}}</h5> - <h4> - <a ui-sref="app.wot_identity({pubkey: peer.pubkey, uid: peer.uid})"> - <span ng-if="peer.uid" class="positive"> - <i class="ion-person"></i> - {{peer.name||peer.uid}} - </span> - <span ng-if="!peer.uid" class="gray" title="{{peer.pubkey}}"><i class="ion-key"></i> {{peer.pubkey|formatPubkey}} |</span> - </a> - <span ng-if="!peer.uid" class="assertive">{{'PEER.MIRROR'|translate}}</span> - </h4> - - - <span class="badge" ng-class="{'badge-balanced': peer.hasMainConsensusBlock, 'badge-energized': peer.hasConsensusBlock }">{{peer.currentNumber|formatInteger}}</span> - <span class="badge badge-secondary" ng-if="peer.consensusBlockDelta && expertMode"> - <i class="ion-clock"></i> - {{peer.consensusBlockDelta|formatDurationTime}}</span> - -</div> - diff --git a/www/plugins/map/templates/network/view_map.html b/www/plugins/map/templates/network/view_map.html index 1e5338cfffd48c7ad290ae3de0c574870c1ecb0f..7fcf70841496471e9c1127cd8ebaa0df8b6bb830 100644 --- a/www/plugins/map/templates/network/view_map.html +++ b/www/plugins/map/templates/network/view_map.html @@ -1,4 +1,4 @@ -<ion-view left-buttons="leftButtons"> +<ion-view left-buttons="leftButtons" class="view-map-network"> <ion-nav-title> </ion-nav-title> diff --git a/www/templates/network/item_content_peer.html b/www/templates/network/item_content_peer.html new file mode 100644 index 0000000000000000000000000000000000000000..8f129a959332d5fdbf9108a2e78b67e8543cb27f --- /dev/null +++ b/www/templates/network/item_content_peer.html @@ -0,0 +1,55 @@ + + <i class="icon ion-android-desktop" + ng-class=":rebind:{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" + ng-if=":rebind:!peer.uid || !peer.avatar"></i> + <b class="icon-secondary ion-person" ng-if=":rebind:!peer.avatar && peer.uid" + ng-class=":rebind:{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" + style="left: 26px; top: -3px;"></b> + <i class="avatar" ng-if=":rebind:peer.uid && peer.avatar" style="background-image: url('{{:rebind:peer.avatar.src}}')"></i> + <b class="icon-secondary assertive ion-close-circled" ng-if=":rebind:!peer.online" style="left: 37px; top: -10px;"></b> + + <div class="row no-padding"> + <div class="col no-padding"> + <h3 class="dark"> + {{:rebind:peer.dns || peer.server}} + </h3> + <h4> + <span class="gray" ng-if=":rebind:!peer.uid"> + <i class="ion-key"></i> {{:rebind:peer.pubkey|formatPubkey}} + </span> + <span class="positive" ng-if=":rebind:peer.uid"> + <i class="ion-person"></i> {{:rebind:peer.name || peer.uid}} + </span> + <span class="gray">{{:rebind:peer.dns && (' | ' + peer.server)}}</span> + </h4> + </div> + <div class="col col-15 no-padding text-center hidden-xs hidden-sm" ng-if="::expertMode"> + <div style="min-width: 50px; padding-top: 5px;" > + <span ng-if="peer.isSsl()"> + <i class="ion-locked"></i> <small>SSL</small> + </span> + </div> + <div ng-if=":rebind:peer.hasEndpoint('ES_USER_API')" ng-click="showEndpointsPopover($event, peer, 'ES_USER_API')"> + <i class="ion-es-user-api"></i> + <b class="ion-plus dark" style="position: relative; left: -14px; top:-17px; font-size : 16px;"></b> + </div> + <div ng-if=":rebind:peer.isTor()" ng-click="showEndpointsPopover($event, peer, 'BMATOR')"> + <i class="ion-bma-tor-api"></i> + </div> + </div> + <div class="col col-20 no-padding text-center" ng-if="::expertMode && search.type != 'offline'"> + <h3 class="hidden-sm hidden-xs gray"> + <span ng-if=":rebind:peer.uid"><i class="ion-lock-combination"></i>{{:rebind:peer.difficulty||'?'}}</span> + <span ng-if=":rebind:!peer.uid" translate>PEER.MIRROR</span> + </h3> + <h4 class="hidden-sm hidden-xs gray">{{:rebind: peer.version ? ('v'+peer.version) : ''}}</h4> + </div> + <div class="col col-20 no-padding text-center"> + <span id="{{$index === 0 ? helptipPrefix + '-peer-0-block' : ''}}" + class="badge" ng-class=":rebind:{'badge-balanced': peer.hasMainConsensusBlock, 'badge-energized': peer.hasConsensusBlock }">{{:rebind:peer.currentNumber|formatInteger}}</span> + <span class="badge badge-secondary" ng-if=":rebind:peer.consensusBlockDelta && expertMode"> + <i class="ion-clock"></i> + {{:rebind:peer.consensusBlockDelta|formatDurationTime}}</span> + + </div> + </div> diff --git a/www/templates/network/items_peers.html b/www/templates/network/items_peers.html index 3e489af5a8ab1f7f78325a59113ac7f407df96ed..8da1de7571fed875d91a6fe9c9c71a05d0f59c87 100644 --- a/www/templates/network/items_peers.html +++ b/www/templates/network/items_peers.html @@ -1,5 +1,4 @@ -<!-- bind-notifier="{ rebind:search.results}" --> -<bind-notifier ng-class="::motion.ionListClass"> +<div ng-class="::motion.ionListClass" class="no-padding"> <div class="item item-text-wrap no-border done in gray no-padding-top no-padding-bottom" ng-if="isHttps && expertMode"> <small><i class="icon ion-alert-circled"></i> {{'NETWORK.INFO.ONLY_SSL_PEERS'|translate}}</small> @@ -29,63 +28,9 @@ <div ng-repeat="peer in :rebind:search.results track by peer.id" class="item item-peer item-icon-left ink" ng-class="::ionItemClass" - id="helptip-network-peer-{{$index}}" - ng-click="selectPeer(peer)"> - - <i class="icon ion-android-desktop" - ng-class=":rebind:{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" - ng-if=":rebind:!peer.uid || !peer.avatar"></i> - <b class="icon-secondary ion-person" ng-if=":rebind:!peer.avatar && peer.uid" - ng-class=":rebind:{'balanced': peer.online && peer.hasMainConsensusBlock, 'energized': peer.online && peer.hasConsensusBlock, 'gray': peer.online && !peer.hasConsensusBlock && !peer.hasMainConsensusBlock, 'stable': !peer.online}" - style="left: 26px; top: -3px;"></b> - <i class="avatar" ng-if=":rebind:peer.uid && peer.avatar" style="background-image: url('{{:rebind:peer.avatar.src}}')"></i> - <b class="icon-secondary assertive ion-close-circled" ng-if=":rebind:!peer.online" style="left: 37px; top: -10px;"></b> - - <div class="row no-padding"> - <div class="col no-padding"> - <h3 class="dark"> - {{:rebind:peer.dns || peer.server}} - </h3> - <h4> - <span class="gray" ng-if=":rebind:!peer.uid"> - <i class="ion-key"></i> {{:rebind:peer.pubkey|formatPubkey}} - </span> - <span class="positive" ng-if=":rebind:peer.uid"> - <i class="ion-person"></i> {{:rebind:peer.name || peer.uid}} - </span> - <span class="gray">{{:rebind:peer.dns && (' | ' + peer.server)}}</span> - </h4> - </div> - <div class="col col-15 no-padding text-center hidden-xs hidden-sm" ng-if="::expertMode"> - <div style="min-width: 50px; padding-top: 5px;" > - <span ng-if="peer.isSsl()"> - <i class="ion-locked"></i> <small>SSL</small> - </span> - </div> - <div ng-if=":rebind:peer.hasEndpoint('ES_USER_API')" ng-click="showEndpointsPopover($event, peer, 'ES_USER_API')"> - <i class="ion-es-user-api"></i> - <b class="ion-plus dark" style="position: relative; left: -14px; top:-17px; font-size : 16px;"></b> - </div> - <div ng-if=":rebind:peer.isTor()" ng-click="showEndpointsPopover($event, peer, 'BMATOR')"> - <i class="ion-bma-tor-api"></i> - </div> - </div> - <div class="col col-20 no-padding text-center" ng-if="::expertMode && search.type != 'offline'"> - <h3 class="hidden-sm hidden-xs gray"> - <span ng-if=":rebind:peer.uid"><i class="ion-lock-combination"></i>{{:rebind:peer.difficulty||'?'}}</span> - <span ng-if=":rebind:!peer.uid" translate>PEER.MIRROR</span> - </h3> - <h4 class="hidden-sm hidden-xs gray">{{:rebind: peer.version ? ('v'+peer.version) : ''}}</h4> - </div> - <div class="col col-20 no-padding text-center"> - <span id="helptip-network-peer-{{$index}}-block" - class="badge" ng-class=":rebind:{'badge-balanced': peer.hasMainConsensusBlock, 'badge-energized': peer.hasConsensusBlock }">{{:rebind:peer.currentNumber|formatInteger}}</span> - <span class="badge badge-secondary" ng-if=":rebind:peer.consensusBlockDelta && expertMode"> - <i class="ion-clock"></i> - {{:rebind:peer.consensusBlockDelta|formatDurationTime}}</span> - - </div> - </div> + id="{{helptipPrefix}}-peer-{{$index}}" + ng-click="selectPeer(peer)" + ng-include="'templates/network/item_content_peer.html'"> </div> -</bind-notifier> +</div>