Skip to content
Snippets Groups Projects
Commit c8a3ee98 authored by Benoit Lavenier's avatar Benoit Lavenier
Browse files

[enh] Network better peer popup

parent c89d82bd
No related branches found
No related tags found
No related merge requests found
...@@ -20,7 +20,8 @@ ...@@ -20,7 +20,8 @@
"ui-leaflet": "^2.0.0", "ui-leaflet": "^2.0.0",
"leaflet-search": "^2.7.2", "leaflet-search": "^2.7.2",
"angular-leaflet-directive": "angular-leaflet#^0.10.0", "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": { "resolutions": {
"angular-sanitize": "1.5.3", "angular-sanitize": "1.5.3",
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<!--endRemoveIf(no-device)--> <!--endRemoveIf(no-device)-->
<!--removeIf(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-search/dist/leaflet-search.src.css">
<link rel="stylesheet" type="text/css" href="lib/leaflet.loading/src/Control.Loading.css">
<!--endRemoveIf(device)--> <!--endRemoveIf(device)-->
<link rel="stylesheet" href="lib/Leaflet.EasyButton/src/easy-button.css"> <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"> <link rel="stylesheet" type="text/css" href="dist/dist_css/plugins/es/css/style.css">
...@@ -89,6 +90,7 @@ ...@@ -89,6 +90,7 @@
<script src="lib/Leaflet.awesome-markers/dist/leaflet.awesome-markers.js"></script> <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-search/dist/leaflet-search.src.js"></script>
<script src="lib/Leaflet.EasyButton/src/easy-button.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)--> <!--endRemoveIf(no-plugin)-->
<!-- endbuild --> <!-- endbuild -->
......
...@@ -106,6 +106,7 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho ...@@ -106,6 +106,7 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho
$scope.searchTextId = null; $scope.searchTextId = null;
$scope.ionItemClass = 'item-border-large'; $scope.ionItemClass = 'item-border-large';
$scope.defaultSizeLimit = UIUtils.screen.isSmall() ? 50 : 100; $scope.defaultSizeLimit = UIUtils.screen.isSmall() ? 50 : 100;
$scope.helptipPrefix = 'helptip-network';
/** /**
* Enter into the view * Enter into the view
......
...@@ -56,12 +56,12 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser ...@@ -56,12 +56,12 @@ angular.module('cesium.es.geo.services', ['cesium.services', 'cesium.es.http.ser
function searchPositionByIP(ip) { function searchPositionByIP(ip) {
var now = new Date(); //var now = new Date();
console.debug('[ES] [geo] Searching IP position [{0}]...'.format(ip)); //console.debug('[ES] [geo] Searching IP position [{0}]...'.format(ip));
return that.raw.searchByIP({ip: ip}) return that.raw.searchByIP({ip: ip})
.then(function(res) { .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; return res ? {lat: res.latitude,lng: res.longitude} : undefined;
}); });
} }
......
...@@ -56,5 +56,32 @@ ...@@ -56,5 +56,32 @@
} }
.leaflet-popup .item-peer { .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;
} }
...@@ -44,29 +44,39 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map ...@@ -44,29 +44,39 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
var var
formatPubkey = $filter('formatPubkey'), formatPubkey = $filter('formatPubkey'),
markerTemplate = $templateCache.get('plugins/map/templates/network/popup_marker.html'), markerMessageTemplate,
// Create a hidden layer, to hold search markers // Create a hidden layer, to hold search markers
searchLayer = L.layerGroup({visible: false}), searchLayer = L.layerGroup({visible: false}),
loadingControl,
icons= { icons= {
member: { member: {
type: 'awesomeMarker', type: 'awesomeMarker',
icon: 'person', icon: 'person',
markerColor: 'blue' markerColor: 'green'
}, },
mirror: { mirror: {
type: 'awesomeMarker', type: 'awesomeMarker',
icon: 'android-desktop', icon: 'android-desktop',
markerColor: 'lightgray' markerColor: 'green'
}, },
offline: { offline: {
type: 'awesomeMarker', type: 'awesomeMarker',
icon: 'ion-close-circled', icon: 'ion-close-circled',
markerColor: 'red' 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.mapId = 'map-network-' + $scope.$id;
$scope.helptipPrefix = 'helptip-' + $scope.mapId; // make to override, to avoid error during help tour
$scope.map = MapUtils.map({ $scope.map = MapUtils.map({
layers: { layers: {
...@@ -87,14 +97,14 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map ...@@ -87,14 +97,14 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
visible: false visible: false
} }
} }
} },
markers: {}
}); });
var inheritedEnter = $scope.enter; var inheritedEnter = $scope.enter;
$scope.enter = function(e, state) { $scope.enter = function(e, state) {
if ($scope.networkStarted) return; if ($scope.networkStarted) return;
console.log("[map] [network] Opening the view...");
// remember state, to be able to refresh location // remember state, to be able to refresh location
$scope.stateName = state && state.stateName; $scope.stateName = state && state.stateName;
$scope.stateParams = angular.copy(state && state.stateParams||{}); $scope.stateParams = angular.copy(state && state.stateParams||{});
...@@ -152,95 +162,99 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map ...@@ -152,95 +162,99 @@ angular.module('cesium.map.network.controllers', ['cesium.services', 'cesium.map
if (!$scope.search.results || !$scope.search.results.length) return; // nothing 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 // skip TOR peer
if (peer.isTor()) return res; // already define if (peer.isTor()) return; // already define
if (peer.lat && peer.lng) return res.concat(peer); // 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(); var ip = peer.getHost();
return res.concat(esGeo.point.searchByIP(ip) esGeo.point.searchByIP(ip)
// Create the marker
.then(function(position){ .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) { .catch(function(err) {
console.debug('No position found for IP ['+ip+']', 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) // Remove old markers not found in the new result
.then(function(map) { _.forEach(_.keys(markerIdByPeerIdToRemove), function(peerId) {
delete markerIdByPeerId[peerId];
$scope.map.markers = markers; });
_.forEach(_.values(markerIdByPeerIdToRemove), function(markerId) {
// Update map center (if need) delete $scope.map.markers[markerId];
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);
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;
}
});
}; };
}); });
<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>&nbsp;
{{peer.consensusBlockDelta|formatDurationTime}}</span>
</div>
<ion-view left-buttons="leftButtons"> <ion-view left-buttons="leftButtons" class="view-map-network">
<ion-nav-title> <ion-nav-title>
</ion-nav-title> </ion-nav-title>
......
<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>&nbsp;
{{:rebind:peer.consensusBlockDelta|formatDurationTime}}</span>
</div>
</div>
<!-- bind-notifier="{ rebind:search.results}" --> <div ng-class="::motion.ionListClass" class="no-padding">
<bind-notifier ng-class="::motion.ionListClass">
<div class="item item-text-wrap no-border done in gray no-padding-top no-padding-bottom" ng-if="isHttps && expertMode"> <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> <small><i class="icon ion-alert-circled"></i> {{'NETWORK.INFO.ONLY_SSL_PEERS'|translate}}</small>
...@@ -29,63 +28,9 @@ ...@@ -29,63 +28,9 @@
<div ng-repeat="peer in :rebind:search.results track by peer.id" <div ng-repeat="peer in :rebind:search.results track by peer.id"
class="item item-peer item-icon-left ink" class="item item-peer item-icon-left ink"
ng-class="::ionItemClass" ng-class="::ionItemClass"
id="helptip-network-peer-{{$index}}" id="{{helptipPrefix}}-peer-{{$index}}"
ng-click="selectPeer(peer)"> ng-click="selectPeer(peer)"
ng-include="'templates/network/item_content_peer.html'">
<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>&nbsp;
{{:rebind:peer.consensusBlockDelta|formatDurationTime}}</span>
</div>
</div>
</div> </div>
</bind-notifier> </div>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment