mirror of
https://github.com/mempool/mempool.git
synced 2025-02-22 14:22:44 +01:00
Create geolocation component to format geolocation data
This commit is contained in:
parent
50d99634f7
commit
e437f2125d
15 changed files with 263 additions and 40 deletions
|
@ -284,9 +284,10 @@ class NodesApi {
|
||||||
public async $getNodesPerCountry(countryId: string) {
|
public async $getNodesPerCountry(countryId: string) {
|
||||||
try {
|
try {
|
||||||
const query = `
|
const query = `
|
||||||
SELECT nodes.public_key, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, CAST(COALESCE(node_stats.channels, 0) as INT) as channels,
|
SELECT nodes.public_key, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, CAST(COALESCE(node_stats.channels, 0) as INT) as channels,
|
||||||
nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at,
|
nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at,
|
||||||
geo_names_city.names as city
|
geo_names_city.names as city, geo_names_country.names as country,
|
||||||
|
geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision
|
||||||
FROM node_stats
|
FROM node_stats
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT public_key, MAX(added) as last_added
|
SELECT public_key, MAX(added) as last_added
|
||||||
|
@ -294,15 +295,19 @@ class NodesApi {
|
||||||
GROUP BY public_key
|
GROUP BY public_key
|
||||||
) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added
|
) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added
|
||||||
RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key
|
RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key
|
||||||
JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country'
|
LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country'
|
||||||
LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city'
|
LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city'
|
||||||
|
LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code'
|
||||||
|
LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division'
|
||||||
WHERE geo_names_country.id = ?
|
WHERE geo_names_country.id = ?
|
||||||
ORDER BY capacity DESC
|
ORDER BY capacity DESC
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const [rows]: any = await DB.query(query, [countryId]);
|
const [rows]: any = await DB.query(query, [countryId]);
|
||||||
for (let i = 0; i < rows.length; ++i) {
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
|
rows[i].country = JSON.parse(rows[i].country);
|
||||||
rows[i].city = JSON.parse(rows[i].city);
|
rows[i].city = JSON.parse(rows[i].city);
|
||||||
|
rows[i].subdivision = JSON.parse(rows[i].subdivision);
|
||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -316,7 +321,8 @@ class NodesApi {
|
||||||
const query = `
|
const query = `
|
||||||
SELECT nodes.public_key, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, CAST(COALESCE(node_stats.channels, 0) as INT) as channels,
|
SELECT nodes.public_key, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, CAST(COALESCE(node_stats.channels, 0) as INT) as channels,
|
||||||
nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at,
|
nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at,
|
||||||
geo_names_city.names as city, geo_names_country.names as country
|
geo_names_city.names as city, geo_names_country.names as country,
|
||||||
|
geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision
|
||||||
FROM node_stats
|
FROM node_stats
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT public_key, MAX(added) as last_added
|
SELECT public_key, MAX(added) as last_added
|
||||||
|
@ -324,8 +330,10 @@ class NodesApi {
|
||||||
GROUP BY public_key
|
GROUP BY public_key
|
||||||
) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added
|
) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added
|
||||||
RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key
|
RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key
|
||||||
JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country'
|
LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country'
|
||||||
LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city'
|
LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city'
|
||||||
|
LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code'
|
||||||
|
LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division'
|
||||||
WHERE nodes.as_number IN (?)
|
WHERE nodes.as_number IN (?)
|
||||||
ORDER BY capacity DESC
|
ORDER BY capacity DESC
|
||||||
`;
|
`;
|
||||||
|
@ -334,6 +342,7 @@ class NodesApi {
|
||||||
for (let i = 0; i < rows.length; ++i) {
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
rows[i].country = JSON.parse(rows[i].country);
|
rows[i].country = JSON.parse(rows[i].country);
|
||||||
rows[i].city = JSON.parse(rows[i].city);
|
rows[i].city = JSON.parse(rows[i].city);
|
||||||
|
rows[i].subdivision = JSON.parse(rows[i].subdivision);
|
||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Observable } from 'rxjs';
|
||||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { OpenGraphService } from 'src/app/services/opengraph.service';
|
import { OpenGraphService } from 'src/app/services/opengraph.service';
|
||||||
import { getFlagEmoji } from 'src/app/shared/graphs.utils';
|
import { getFlagEmoji } from 'src/app/shared/common.utils';
|
||||||
import { LightningApiService } from '../lightning-api.service';
|
import { LightningApiService } from '../lightning-api.service';
|
||||||
import { isMobile } from '../../shared/common.utils';
|
import { isMobile } from '../../shared/common.utils';
|
||||||
|
|
||||||
|
|
|
@ -42,24 +42,10 @@
|
||||||
<app-fiat [value]="node.avgCapacity" digitsInfo="1.0-0"></app-fiat>
|
<app-fiat [value]="node.avgCapacity" digitsInfo="1.0-0"></app-fiat>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="node.country && node.city && node.subdivision">
|
<tr *ngIf="node.geolocation">
|
||||||
<td i18n="location">Location</td>
|
<td i18n="location">Location</td>
|
||||||
<td>
|
<td>
|
||||||
<span>{{ node.city.en }}, {{ node.subdivision.en }}</span>
|
<app-geolocation [data]="node.geolocation" [type]="'node'"></app-geolocation>
|
||||||
<br>
|
|
||||||
<a class="d-flex align-items-center" [routerLink]="['/lightning/nodes/country' | relativeUrl, node.iso_code]">
|
|
||||||
<span class="link">{{ node.country.en }}</span>
|
|
||||||
|
|
||||||
<span class="flag">{{ node.flag }}</span>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr *ngIf="node.country && !node.city">
|
|
||||||
<td i18n="location">Location</td>
|
|
||||||
<td>
|
|
||||||
<a [routerLink]="['/lightning/nodes/country' | relativeUrl, node.iso_code]">
|
|
||||||
{{ node.country.en }} {{ node.flag }}
|
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { getFlagEmoji } from 'src/app/shared/graphs.utils';
|
|
||||||
import { LightningApiService } from '../lightning-api.service';
|
import { LightningApiService } from '../lightning-api.service';
|
||||||
import { isMobile } from '../../shared/common.utils';
|
import { isMobile } from '../../shared/common.utils';
|
||||||
|
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-node',
|
selector: 'app-node',
|
||||||
|
@ -58,7 +58,6 @@ export class NodeComponent implements OnInit {
|
||||||
} else if (socket.indexOf('onion') > -1) {
|
} else if (socket.indexOf('onion') > -1) {
|
||||||
label = 'Tor';
|
label = 'Tor';
|
||||||
}
|
}
|
||||||
node.flag = getFlagEmoji(node.iso_code);
|
|
||||||
socketsObject.push({
|
socketsObject.push({
|
||||||
label: label,
|
label: label,
|
||||||
socket: node.public_key + '@' + socket,
|
socket: node.public_key + '@' + socket,
|
||||||
|
@ -66,6 +65,19 @@ export class NodeComponent implements OnInit {
|
||||||
}
|
}
|
||||||
node.socketsObject = socketsObject;
|
node.socketsObject = socketsObject;
|
||||||
node.avgCapacity = node.capacity / Math.max(1, node.active_channel_count);
|
node.avgCapacity = node.capacity / Math.max(1, node.active_channel_count);
|
||||||
|
|
||||||
|
if (!node?.country && !node?.city &&
|
||||||
|
!node?.subdivision && !node?.iso) {
|
||||||
|
node.geolocation = null;
|
||||||
|
} else {
|
||||||
|
node.geolocation = <GeolocationData>{
|
||||||
|
country: node.country?.en,
|
||||||
|
city: node.city?.en,
|
||||||
|
subdivision: node.subdivision?.en,
|
||||||
|
iso: node.iso_code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}),
|
}),
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { StateService } from 'src/app/services/state.service';
|
||||||
import { download } from 'src/app/shared/graphs.utils';
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe';
|
import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe';
|
||||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { getFlagEmoji } from 'src/app/shared/graphs.utils';
|
import { getFlagEmoji } from 'src/app/shared/common.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nodes-per-country-chart',
|
selector: 'app-nodes-per-country-chart',
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
{{ node.channels }}
|
{{ node.channels }}
|
||||||
</td>
|
</td>
|
||||||
<td class="city text-right text-truncate">
|
<td class="city text-right text-truncate">
|
||||||
{{ node?.city?.en ?? '-' }}
|
<app-geolocation [data]="node.geolocation" [type]="'list-country'"></app-geolocation>
|
||||||
</td>
|
</td>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { ActivatedRoute } from '@angular/router';
|
||||||
import { map, Observable } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { getFlagEmoji } from 'src/app/shared/graphs.utils';
|
import { getFlagEmoji } from 'src/app/shared/common.utils';
|
||||||
|
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nodes-per-country',
|
selector: 'app-nodes-per-country',
|
||||||
|
@ -29,6 +30,16 @@ export class NodesPerCountry implements OnInit {
|
||||||
name: response.country.en,
|
name: response.country.en,
|
||||||
flag: getFlagEmoji(this.route.snapshot.params.country)
|
flag: getFlagEmoji(this.route.snapshot.params.country)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const i in response.nodes) {
|
||||||
|
response.nodes[i].geolocation = <GeolocationData>{
|
||||||
|
country: response.nodes[i].country?.en,
|
||||||
|
city: response.nodes[i].city?.en,
|
||||||
|
subdivision: response.nodes[i].subdivision?.en,
|
||||||
|
iso: response.nodes[i].iso_code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.seoService.setTitle($localize`Lightning nodes in ${this.country.name}`);
|
this.seoService.setTitle($localize`Lightning nodes in ${this.country.name}`);
|
||||||
return response.nodes;
|
return response.nodes;
|
||||||
})
|
})
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
{{ node.channels }}
|
{{ node.channels }}
|
||||||
</td>
|
</td>
|
||||||
<td class="city text-right text-truncate">
|
<td class="city text-right text-truncate">
|
||||||
{{ node?.city?.en ?? '-' }}
|
<app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>
|
||||||
</td>
|
</td>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||||
import { map, Observable } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nodes-per-isp',
|
selector: 'app-nodes-per-isp',
|
||||||
|
@ -29,6 +30,16 @@ export class NodesPerISP implements OnInit {
|
||||||
id: this.route.snapshot.params.isp
|
id: this.route.snapshot.params.isp
|
||||||
};
|
};
|
||||||
this.seoService.setTitle($localize`Lightning nodes on ISP: ${response.isp} [AS${this.route.snapshot.params.isp}]`);
|
this.seoService.setTitle($localize`Lightning nodes on ISP: ${response.isp} [AS${this.route.snapshot.params.isp}]`);
|
||||||
|
|
||||||
|
for (const i in response.nodes) {
|
||||||
|
response.nodes[i].geolocation = <GeolocationData>{
|
||||||
|
country: response.nodes[i].country?.en,
|
||||||
|
city: response.nodes[i].city?.en,
|
||||||
|
subdivision: response.nodes[i].subdivision?.en,
|
||||||
|
iso: response.nodes[i].iso_code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return response.nodes;
|
return response.nodes;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,120 @@
|
||||||
export function isMobile() {
|
export function isMobile(): boolean {
|
||||||
return (window.innerWidth <= 767.98);
|
return (window.innerWidth <= 767.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFlagEmoji(countryCode): string {
|
||||||
|
if (!countryCode) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const codePoints = countryCode
|
||||||
|
.toUpperCase()
|
||||||
|
.split('')
|
||||||
|
.map(char => 127397 + char.charCodeAt());
|
||||||
|
return String.fromCodePoint(...codePoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://gist.github.com/calebgrove/c285a9510948b633aa47
|
||||||
|
export function convertRegion(input, to: 'name' | 'abbreviated'): string {
|
||||||
|
if (!input) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const states = [
|
||||||
|
['Alabama', 'AL'],
|
||||||
|
['Alaska', 'AK'],
|
||||||
|
['American Samoa', 'AS'],
|
||||||
|
['Arizona', 'AZ'],
|
||||||
|
['Arkansas', 'AR'],
|
||||||
|
['Armed Forces Americas', 'AA'],
|
||||||
|
['Armed Forces Europe', 'AE'],
|
||||||
|
['Armed Forces Pacific', 'AP'],
|
||||||
|
['California', 'CA'],
|
||||||
|
['Colorado', 'CO'],
|
||||||
|
['Connecticut', 'CT'],
|
||||||
|
['Delaware', 'DE'],
|
||||||
|
['District Of Columbia', 'DC'],
|
||||||
|
['Florida', 'FL'],
|
||||||
|
['Georgia', 'GA'],
|
||||||
|
['Guam', 'GU'],
|
||||||
|
['Hawaii', 'HI'],
|
||||||
|
['Idaho', 'ID'],
|
||||||
|
['Illinois', 'IL'],
|
||||||
|
['Indiana', 'IN'],
|
||||||
|
['Iowa', 'IA'],
|
||||||
|
['Kansas', 'KS'],
|
||||||
|
['Kentucky', 'KY'],
|
||||||
|
['Louisiana', 'LA'],
|
||||||
|
['Maine', 'ME'],
|
||||||
|
['Marshall Islands', 'MH'],
|
||||||
|
['Maryland', 'MD'],
|
||||||
|
['Massachusetts', 'MA'],
|
||||||
|
['Michigan', 'MI'],
|
||||||
|
['Minnesota', 'MN'],
|
||||||
|
['Mississippi', 'MS'],
|
||||||
|
['Missouri', 'MO'],
|
||||||
|
['Montana', 'MT'],
|
||||||
|
['Nebraska', 'NE'],
|
||||||
|
['Nevada', 'NV'],
|
||||||
|
['New Hampshire', 'NH'],
|
||||||
|
['New Jersey', 'NJ'],
|
||||||
|
['New Mexico', 'NM'],
|
||||||
|
['New York', 'NY'],
|
||||||
|
['North Carolina', 'NC'],
|
||||||
|
['North Dakota', 'ND'],
|
||||||
|
['Northern Mariana Islands', 'NP'],
|
||||||
|
['Ohio', 'OH'],
|
||||||
|
['Oklahoma', 'OK'],
|
||||||
|
['Oregon', 'OR'],
|
||||||
|
['Pennsylvania', 'PA'],
|
||||||
|
['Puerto Rico', 'PR'],
|
||||||
|
['Rhode Island', 'RI'],
|
||||||
|
['South Carolina', 'SC'],
|
||||||
|
['South Dakota', 'SD'],
|
||||||
|
['Tennessee', 'TN'],
|
||||||
|
['Texas', 'TX'],
|
||||||
|
['US Virgin Islands', 'VI'],
|
||||||
|
['Utah', 'UT'],
|
||||||
|
['Vermont', 'VT'],
|
||||||
|
['Virginia', 'VA'],
|
||||||
|
['Washington', 'WA'],
|
||||||
|
['West Virginia', 'WV'],
|
||||||
|
['Wisconsin', 'WI'],
|
||||||
|
['Wyoming', 'WY'],
|
||||||
|
];
|
||||||
|
|
||||||
|
// So happy that Canada and the US have distinct abbreviations
|
||||||
|
const provinces = [
|
||||||
|
['Alberta', 'AB'],
|
||||||
|
['British Columbia', 'BC'],
|
||||||
|
['Manitoba', 'MB'],
|
||||||
|
['New Brunswick', 'NB'],
|
||||||
|
['Newfoundland', 'NF'],
|
||||||
|
['Northwest Territory', 'NT'],
|
||||||
|
['Nova Scotia', 'NS'],
|
||||||
|
['Nunavut', 'NU'],
|
||||||
|
['Ontario', 'ON'],
|
||||||
|
['Prince Edward Island', 'PE'],
|
||||||
|
['Quebec', 'QC'],
|
||||||
|
['Saskatchewan', 'SK'],
|
||||||
|
['Yukon', 'YT'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const regions = states.concat(provinces);
|
||||||
|
|
||||||
|
let i; // Reusable loop variable
|
||||||
|
if (to == 'abbreviated') {
|
||||||
|
input = input.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); });
|
||||||
|
for (i = 0; i < regions.length; i++) {
|
||||||
|
if (regions[i][0] == input) {
|
||||||
|
return (regions[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (to == 'name') {
|
||||||
|
input = input.toUpperCase();
|
||||||
|
for (i = 0; i < regions.length; i++) {
|
||||||
|
if (regions[i][1] == input) {
|
||||||
|
return (regions[i][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<span [innerHTML]="formattedLocation"></span>
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { Component, Input, OnChanges } from '@angular/core';
|
||||||
|
import { convertRegion, getFlagEmoji } from '../../common.utils';
|
||||||
|
|
||||||
|
export interface GeolocationData {
|
||||||
|
country: string;
|
||||||
|
city: string;
|
||||||
|
subdivision: string;
|
||||||
|
iso: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-geolocation',
|
||||||
|
templateUrl: './geolocation.component.html',
|
||||||
|
styleUrls: ['./geolocation.component.scss']
|
||||||
|
})
|
||||||
|
export class GeolocationComponent implements OnChanges {
|
||||||
|
@Input() data: GeolocationData;
|
||||||
|
@Input() type: 'node' | 'list-isp' | 'list-country';
|
||||||
|
|
||||||
|
formattedLocation: string = '';
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
const city = this.data.city ? this.data.city : '';
|
||||||
|
const subdivisionLikeCity = this.data.city === this.data.subdivision;
|
||||||
|
let subdivision = this.data.subdivision;
|
||||||
|
|
||||||
|
if (['US', 'CA'].includes(this.data.iso) === false || (this.type === 'node' && subdivisionLikeCity)) {
|
||||||
|
this.data.subdivision = undefined;
|
||||||
|
} else if (['list-isp', 'list-country'].includes(this.type) === true) {
|
||||||
|
subdivision = convertRegion(this.data.subdivision, 'abbreviated');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'list-country') {
|
||||||
|
if (this.data.city) {
|
||||||
|
this.formattedLocation += ' ' + city;
|
||||||
|
if (this.data.subdivision) {
|
||||||
|
this.formattedLocation += ', ' + subdivision;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.formattedLocation += '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'list-isp') {
|
||||||
|
this.formattedLocation = getFlagEmoji(this.data.iso);
|
||||||
|
if (this.data.city) {
|
||||||
|
this.formattedLocation += ' ' + city;
|
||||||
|
if (this.data.subdivision) {
|
||||||
|
this.formattedLocation += ', ' + subdivision;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.formattedLocation += ' ' + this.data.country;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'node') {
|
||||||
|
const city = this.data.city ? this.data.city : '';
|
||||||
|
|
||||||
|
// City
|
||||||
|
this.formattedLocation = `${city}`;
|
||||||
|
|
||||||
|
// ,Subdivision
|
||||||
|
if (this.formattedLocation.length > 0 && !subdivisionLikeCity) {
|
||||||
|
this.formattedLocation += ', ';
|
||||||
|
}
|
||||||
|
if (!subdivisionLikeCity) {
|
||||||
|
this.formattedLocation += `${subdivision}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <br>[flag] County
|
||||||
|
if (this.data?.country.length ?? 0 > 0) {
|
||||||
|
if ((this.formattedLocation?.length ?? 0 > 0) && !subdivisionLikeCity) {
|
||||||
|
this.formattedLocation += '<br>';
|
||||||
|
} else if (this.data.city) {
|
||||||
|
this.formattedLocation += ', ';
|
||||||
|
}
|
||||||
|
this.formattedLocation += `${this.data.country} ${getFlagEmoji(this.data.iso)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,13 +91,3 @@ export function detectWebGL() {
|
||||||
return (gl && gl instanceof WebGLRenderingContext);
|
return (gl && gl instanceof WebGLRenderingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFlagEmoji(countryCode) {
|
|
||||||
if (!countryCode) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const codePoints = countryCode
|
|
||||||
.toUpperCase()
|
|
||||||
.split('')
|
|
||||||
.map(char => 127397 + char.charCodeAt());
|
|
||||||
return String.fromCodePoint(...codePoints);
|
|
||||||
}
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ import { SatsComponent } from './components/sats/sats.component';
|
||||||
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
|
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
|
||||||
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
||||||
import { ToggleComponent } from './components/toggle/toggle.component';
|
import { ToggleComponent } from './components/toggle/toggle.component';
|
||||||
|
import { GeolocationComponent } from '../shared/components/geolocation/geolocation.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -158,6 +159,7 @@ import { ToggleComponent } from './components/toggle/toggle.component';
|
||||||
SearchResultsComponent,
|
SearchResultsComponent,
|
||||||
TimestampComponent,
|
TimestampComponent,
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
|
GeolocationComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -261,6 +263,7 @@ import { ToggleComponent } from './components/toggle/toggle.component';
|
||||||
SearchResultsComponent,
|
SearchResultsComponent,
|
||||||
TimestampComponent,
|
TimestampComponent,
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
|
GeolocationComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule {
|
export class SharedModule {
|
||||||
|
|
Loading…
Add table
Reference in a new issue