diff --git a/package-lock.json b/package-lock.json index 080a04a..6b671cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,8 @@ "@sentry/react": "^8.31.0", "@tanstack/react-query": "^5.51.15", "@tanstack/react-router": "^1.58.7", - "chart.js": "^4.4.4", "echarts": "^5.5.1", + "dotted-map": "^2.2.3", "idb-keyval": "^6.2.1", "lucide-react": "^0.445.0", "react": "^18.3.1", @@ -1606,6 +1606,37 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz", + "integrity": "sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", + "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", + "dependencies": { + "@turf/helpers": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -2259,6 +2290,15 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, + "node_modules/dotted-map": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dotted-map/-/dotted-map-2.2.3.tgz", + "integrity": "sha512-8hyOOHHLLVCcCisM3yb9hqp+3bJ7TSMcr1SfrUw8Wxp5UMqih35jIvUyagweCooJbz/EH1nC9GGuPysh7+YlAg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.0.1", + "proj4": "^2.6.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "dev": true, @@ -2988,6 +3028,11 @@ "node": ">= 8" } }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -3238,6 +3283,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/proj4": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.12.1.tgz", + "integrity": "sha512-vmhP3hmstjXjzFwg8QXJwpoj4n7GVrXk3ZW3DzNK/Ur4cuwXq7ZiMXaWYvLYLQbX8n4MXgbwTr4lthOUZltBpA==", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "node_modules/punycode": { "version": "2.3.1", "dev": true, @@ -4552,6 +4606,11 @@ "node": ">= 8" } }, + "node_modules/wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==" + }, "node_modules/word-wrap": { "version": "1.2.5", "dev": true, @@ -4603,4 +4662,4 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index a72c073..8586625 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "@sentry/react": "^8.31.0", "@tanstack/react-query": "^5.51.15", "@tanstack/react-router": "^1.58.7", - "chart.js": "^4.4.4", "echarts": "^5.5.1", + "dotted-map": "^2.2.3", "idb-keyval": "^6.2.1", "lucide-react": "^0.445.0", "react": "^18.3.1", diff --git a/src/components/Peers/PeerCountryCell.css b/src/components/Peers/PeerCountryCell.css new file mode 100644 index 0000000..ac159a5 --- /dev/null +++ b/src/components/Peers/PeerCountryCell.css @@ -0,0 +1,5 @@ +.peerCountry { + display: flex; + align-items: center; + gap: 1rem; +} diff --git a/src/components/Peers/PeerCountryCell.tsx b/src/components/Peers/PeerCountryCell.tsx new file mode 100644 index 0000000..1edb332 --- /dev/null +++ b/src/components/Peers/PeerCountryCell.tsx @@ -0,0 +1,73 @@ +import { Cell } from "@codex-storage/marketplace-ui-components"; +import { PeerPin } from "./types"; +import { countriesCoordinates } from "./countries"; +import { useQuery } from "@tanstack/react-query"; +import "./PeerCountryCell.css"; + +export type Props = { + address: string; + onPinAdd: (pin: PeerPin) => void; +}; + +const getFlagEmoji = (countryCode: string) => { + const codePoints = countryCode + .toUpperCase() + .split("") + .map((char) => 127397 + char.charCodeAt(0)); + return String.fromCodePoint(...codePoints); +}; + +export function PeerCountryCell({ address, onPinAdd }: Props) { + const { data } = useQuery({ + queryFn: () => { + const [ip] = address.split(":"); + + return fetch(import.meta.env.VITE_GEO_IP_URL + "/" + ip) + .then((res) => res.json()) + .then((json) => { + const coordinate = countriesCoordinates.find( + (c) => c.iso === json.country + ); + + if (coordinate) { + onPinAdd({ + lat: parseFloat(coordinate.lat), + lng: parseFloat(coordinate.lng), + }); + } + + return coordinate; + }); + }, + queryKey: [address], + + // Enable only when the address exists + enabled: !!address, + + // No need to retry because if the connection to the node + // is back again, all the queries will be invalidated. + retry: false, + + // We can cache the data at Infinity because the relation between + // country and ip is fixed + staleTime: Infinity, + + // Don't expect something new when coming back to the UI + refetchOnWindowFocus: false, + }); + + return ( + +
+ {data ? ( + <> + {!!data && getFlagEmoji(data.iso)} + {data?.name} + + ) : ( + {address} + )} +
+
+ ); +} diff --git a/src/components/Peers/countries.ts b/src/components/Peers/countries.ts new file mode 100644 index 0000000..00151b8 --- /dev/null +++ b/src/components/Peers/countries.ts @@ -0,0 +1,1557 @@ +export const countriesCoordinates = [ + { + "iso": "AF", + "lat": "33", + "lng": "65", + "name": "Afghanistan" + }, + { + "iso": "AX", + "lat": "60.116667", + "lng": "19.9", + "name": "Åland Islands" + }, + { + "iso": "AL", + "lat": "41", + "lng": "20", + "name": "Albania" + }, + { + "iso": "DZ", + "lat": "28", + "lng": "3", + "name": "Algeria" + }, + { + "iso": "AS", + "lat": "-14.3333", + "lng": "-170", + "name": "American Samoa" + }, + { + "iso": "AD", + "lat": "42.5", + "lng": "1.6", + "name": "Andorra" + }, + { + "iso": "AO", + "lat": "-12.5", + "lng": "18.5", + "name": "Angola" + }, + { + "iso": "AI", + "lat": "18.25", + "lng": "-63.1667", + "name": "Anguilla" + }, + { + "iso": "AQ", + "lat": "-90", + "lng": "0", + "name": "Antarctica" + }, + { + "iso": "AG", + "lat": "17.05", + "lng": "-61.8", + "name": "Antigua and Barbuda" + }, + { + "iso": "AR", + "lat": "-34", + "lng": "-64", + "name": "Argentina" + }, + { + "iso": "AM", + "lat": "40", + "lng": "45", + "name": "Armenia" + }, + { + "iso": "AW", + "lat": "12.5", + "lng": "-69.9667", + "name": "Aruba" + }, + { + "iso": "AU", + "lat": "-27", + "lng": "133", + "name": "Australia" + }, + { + "iso": "AT", + "lat": "47.3333", + "lng": "13.3333", + "name": "Austria" + }, + { + "iso": "AZ", + "lat": "40.5", + "lng": "47.5", + "name": "Azerbaijan" + }, + { + "iso": "BS", + "lat": "24.25", + "lng": "-76", + "name": "Bahamas" + }, + { + "iso": "BH", + "lat": "26", + "lng": "50.55", + "name": "Bahrain" + }, + { + "iso": "BD", + "lat": "24", + "lng": "90", + "name": "Bangladesh" + }, + { + "iso": "BB", + "lat": "13.1667", + "lng": "-59.5333", + "name": "Barbados" + }, + { + "iso": "BY", + "lat": "53", + "lng": "28", + "name": "Belarus" + }, + { + "iso": "BE", + "lat": "50.8333", + "lng": "4", + "name": "Belgium" + }, + { + "iso": "BZ", + "lat": "17.25", + "lng": "-88.75", + "name": "Belize" + }, + { + "iso": "BJ", + "lat": "9.5", + "lng": "2.25", + "name": "Benin" + }, + { + "iso": "BM", + "lat": "32.3333", + "lng": "-64.75", + "name": "Bermuda" + }, + { + "iso": "BT", + "lat": "27.5", + "lng": "90.5", + "name": "Bhutan" + }, + { + "iso": "Plurinational State of", + "lat": "68", + "lng": "-17" + }, + { + "iso": "BO", + "lat": "-17", + "lng": "-65", + "name": "Bolivia, Plurinational State of" + }, + { + "iso": "Sint Eustatius and Saba", + "lat": "535", + "lng": "12.183333" + }, + { + "iso": "BA", + "lat": "44", + "lng": "18", + "name": "Bosnia and Herzegovina" + }, + { + "iso": "BW", + "lat": "-22", + "lng": "24", + "name": "Botswana" + }, + { + "iso": "BV", + "lat": "-54.4333", + "lng": "3.4", + "name": "Bouvet Island" + }, + { + "iso": "BR", + "lat": "-10", + "lng": "-55", + "name": "Brazil" + }, + { + "iso": "IO", + "lat": "-6", + "lng": "71.5", + "name": "British Indian Ocean Territory" + }, + { + "iso": "BN", + "lat": "4.5", + "lng": "114.6667", + "name": "Brunei Darussalam" + }, + { + "iso": "BN", + "lat": "4.5", + "lng": "114.6667", + "name": "Brunei Darussalam" + }, + { + "iso": "BG", + "lat": "43", + "lng": "25", + "name": "Bulgaria" + }, + { + "iso": "BF", + "lat": "13", + "lng": "-2", + "name": "Burkina Faso" + }, + { + "iso": "MM", + "lat": "22", + "lng": "98", + "name": "Myanmar" + }, + { + "iso": "BI", + "lat": "-3.5", + "lng": "30", + "name": "Burundi" + }, + { + "iso": "KH", + "lat": "13", + "lng": "105", + "name": "Cambodia" + }, + { + "iso": "CM", + "lat": "6", + "lng": "12", + "name": "Cameroon" + }, + { + "iso": "CA", + "lat": "60", + "lng": "-95", + "name": "Canada" + }, + { + "iso": "CV", + "lat": "16", + "lng": "-24", + "name": "Cape Verde" + }, + { + "iso": "KY", + "lat": "19.5", + "lng": "-80.5", + "name": "Cayman Islands" + }, + { + "iso": "CF", + "lat": "7", + "lng": "21", + "name": "Central African Republic" + }, + { + "iso": "TD", + "lat": "15", + "lng": "19", + "name": "Chad" + }, + { + "iso": "CL", + "lat": "-30", + "lng": "-71", + "name": "Chile" + }, + { + "iso": "CN", + "lat": "35", + "lng": "105", + "name": "China" + }, + { + "iso": "CX", + "lat": "-10.5", + "lng": "105.6667", + "name": "Christmas Island" + }, + { + "iso": "CC", + "lat": "-12.5", + "lng": "96.8333", + "name": "Cocos (Keeling) Islands" + }, + { + "iso": "CO", + "lat": "4", + "lng": "-72", + "name": "Colombia" + }, + { + "iso": "KM", + "lat": "-12.1667", + "lng": "44.25", + "name": "Comoros" + }, + { + "iso": "the Democratic Republic of the", + "lat": "180", + "lng": "0" + }, + { + "iso": "CG", + "lat": "-1", + "lng": "15", + "name": "Congo" + }, + { + "iso": "CK", + "lat": "-21.2333", + "lng": "-159.7667", + "name": "Cook Islands" + }, + { + "iso": "CR", + "lat": "10", + "lng": "-84", + "name": "Costa Rica" + }, + { + "iso": "CI", + "lat": "8", + "lng": "-5", + "name": "Côte d'Ivoire" + }, + { + "iso": "HR", + "lat": "45.1667", + "lng": "15.5", + "name": "Croatia" + }, + { + "iso": "CU", + "lat": "21.5", + "lng": "-80", + "name": "Cuba" + }, + { + "iso": "CW", + "lat": "12.166667", + "lng": "-68.966667", + "name": "Curaçao" + }, + { + "iso": "CY", + "lat": "35", + "lng": "33", + "name": "Cyprus" + }, + { + "iso": "CZ", + "lat": "49.75", + "lng": "15.5", + "name": "Czech Republic" + }, + { + "iso": "DK", + "lat": "56", + "lng": "10", + "name": "Denmark" + }, + { + "iso": "DJ", + "lat": "11.5", + "lng": "43", + "name": "Djibouti" + }, + { + "iso": "DM", + "lat": "15.4167", + "lng": "-61.3333", + "name": "Dominica" + }, + { + "iso": "DO", + "lat": "19", + "lng": "-70.6667", + "name": "Dominican Republic" + }, + { + "iso": "EC", + "lat": "-2", + "lng": "-77.5", + "name": "Ecuador" + }, + { + "iso": "EG", + "lat": "27", + "lng": "30", + "name": "Egypt" + }, + { + "iso": "SV", + "lat": "13.8333", + "lng": "-88.9167", + "name": "El Salvador" + }, + { + "iso": "GQ", + "lat": "2", + "lng": "10", + "name": "Equatorial Guinea" + }, + { + "iso": "ER", + "lat": "15", + "lng": "39", + "name": "Eritrea" + }, + { + "iso": "EE", + "lat": "59", + "lng": "26", + "name": "Estonia" + }, + { + "iso": "ET", + "lat": "8", + "lng": "38", + "name": "Ethiopia" + }, + { + "iso": "FK", + "lat": "-51.75", + "lng": "-59", + "name": "Falkland Islands (Malvinas)" + }, + { + "iso": "FO", + "lat": "62", + "lng": "-7", + "name": "Faroe Islands" + }, + { + "iso": "FJ", + "lat": "-18", + "lng": "175", + "name": "Fiji" + }, + { + "iso": "FI", + "lat": "64", + "lng": "26", + "name": "Finland" + }, + { + "iso": "FR", + "lat": "46", + "lng": "2", + "name": "France" + }, + { + "iso": "GF", + "lat": "4", + "lng": "-53", + "name": "French Guiana" + }, + { + "iso": "PF", + "lat": "-15", + "lng": "-140", + "name": "French Polynesia" + }, + { + "iso": "TF", + "lat": "-43", + "lng": "67", + "name": "French Southern Territories" + }, + { + "iso": "GA", + "lat": "-1", + "lng": "11.75", + "name": "Gabon" + }, + { + "iso": "GM", + "lat": "13.4667", + "lng": "-16.5667", + "name": "Gambia" + }, + { + "iso": "GE", + "lat": "42", + "lng": "43.5", + "name": "Georgia" + }, + { + "iso": "DE", + "lat": "51", + "lng": "9", + "name": "Germany" + }, + { + "iso": "GH", + "lat": "8", + "lng": "-2", + "name": "Ghana" + }, + { + "iso": "GI", + "lat": "36.1833", + "lng": "-5.3667", + "name": "Gibraltar" + }, + { + "iso": "GR", + "lat": "39", + "lng": "22", + "name": "Greece" + }, + { + "iso": "GL", + "lat": "72", + "lng": "-40", + "name": "Greenland" + }, + { + "iso": "GD", + "lat": "12.1167", + "lng": "-61.6667", + "name": "Grenada" + }, + { + "iso": "GP", + "lat": "16.25", + "lng": "-61.5833", + "name": "Guadeloupe" + }, + { + "iso": "GU", + "lat": "13.4667", + "lng": "144.7833", + "name": "Guam" + }, + { + "iso": "GT", + "lat": "15.5", + "lng": "-90.25", + "name": "Guatemala" + }, + { + "iso": "GG", + "lat": "49.5", + "lng": "-2.56", + "name": "Guernsey" + }, + { + "iso": "GW", + "lat": "12", + "lng": "-15", + "name": "Guinea-Bissau" + }, + { + "iso": "GN", + "lat": "11", + "lng": "-10", + "name": "Guinea" + }, + { + "iso": "GY", + "lat": "5", + "lng": "-59", + "name": "Guyana" + }, + { + "iso": "HT", + "lat": "19", + "lng": "-72.4167", + "name": "Haiti" + }, + { + "iso": "HM", + "lat": "-53.1", + "lng": "72.5167", + "name": "Heard Island and McDonald Islands" + }, + { + "iso": "VA", + "lat": "41.9", + "lng": "12.45", + "name": "Holy See (Vatican City State)" + }, + { + "iso": "HN", + "lat": "15", + "lng": "-86.5", + "name": "Honduras" + }, + { + "iso": "HK", + "lat": "22.25", + "lng": "114.1667", + "name": "Hong Kong" + }, + { + "iso": "HU", + "lat": "47", + "lng": "20", + "name": "Hungary" + }, + { + "iso": "IS", + "lat": "65", + "lng": "-18", + "name": "Iceland" + }, + { + "iso": "IN", + "lat": "20", + "lng": "77", + "name": "India" + }, + { + "iso": "ID", + "lat": "-5", + "lng": "120", + "name": "Indonesia" + }, + { + "iso": "Islamic Republic of", + "lat": "364", + "lng": "32" + }, + { + "iso": "IQ", + "lat": "33", + "lng": "44", + "name": "Iraq" + }, + { + "iso": "IE", + "lat": "53", + "lng": "-8", + "name": "Ireland" + }, + { + "iso": "IM", + "lat": "54.23", + "lng": "-4.55", + "name": "Isle of Man" + }, + { + "iso": "IL", + "lat": "31.5", + "lng": "34.75", + "name": "Israel" + }, + { + "iso": "IT", + "lat": "42.8333", + "lng": "12.8333", + "name": "Italy" + }, + { + "iso": "CI", + "lat": "8", + "lng": "-5", + "name": "Côte d'Ivoire" + }, + { + "iso": "JM", + "lat": "18.25", + "lng": "-77.5", + "name": "Jamaica" + }, + { + "iso": "JP", + "lat": "36", + "lng": "138", + "name": "Japan" + }, + { + "iso": "JE", + "lat": "49.21", + "lng": "-2.13", + "name": "Jersey" + }, + { + "iso": "JO", + "lat": "31", + "lng": "36", + "name": "Jordan" + }, + { + "iso": "KZ", + "lat": "48", + "lng": "68", + "name": "Kazakhstan" + }, + { + "iso": "KE", + "lat": "1", + "lng": "38", + "name": "Kenya" + }, + { + "iso": "KI", + "lat": "1.4167", + "lng": "173", + "name": "Kiribati" + }, + { + "iso": "Democratic People's Republic of", + "lat": "408", + "lng": "40" + }, + { + "iso": "Republic of", + "lat": "410", + "lng": "37" + }, + { + "iso": "XK", + "lat": "42.583333", + "lng": "21" + }, + { + "iso": "KW", + "lat": "29.3375", + "lng": "47.6581", + "name": "Kuwait" + }, + { + "iso": "KG", + "lat": "41", + "lng": "75", + "name": "Kyrgyzstan" + }, + { + "iso": "LA", + "lat": "18", + "lng": "105", + "name": "Lao People's Democratic Republic" + }, + { + "iso": "LV", + "lat": "57", + "lng": "25", + "name": "Latvia" + }, + { + "iso": "LB", + "lat": "33.8333", + "lng": "35.8333", + "name": "Lebanon" + }, + { + "iso": "LS", + "lat": "-29.5", + "lng": "28.5", + "name": "Lesotho" + }, + { + "iso": "LR", + "lat": "6.5", + "lng": "-9.5", + "name": "Liberia" + }, + { + "iso": "LY", + "lat": "25", + "lng": "17", + "name": "Libya" + }, + { + "iso": "LY", + "lat": "25", + "lng": "17", + "name": "Libya" + }, + { + "iso": "LI", + "lat": "47.1667", + "lng": "9.5333", + "name": "Liechtenstein" + }, + { + "iso": "LT", + "lat": "56", + "lng": "24", + "name": "Lithuania" + }, + { + "iso": "LU", + "lat": "49.75", + "lng": "6.1667", + "name": "Luxembourg" + }, + { + "iso": "MO", + "lat": "22.1667", + "lng": "113.55", + "name": "Macao" + }, + { + "iso": "the former Yugoslav Republic of", + "lat": "807", + "lng": "41.8333" + }, + { + "iso": "MG", + "lat": "-20", + "lng": "47", + "name": "Madagascar" + }, + { + "iso": "MW", + "lat": "-13.5", + "lng": "34", + "name": "Malawi" + }, + { + "iso": "MY", + "lat": "2.5", + "lng": "112.5", + "name": "Malaysia" + }, + { + "iso": "MV", + "lat": "3.25", + "lng": "73", + "name": "Maldives" + }, + { + "iso": "ML", + "lat": "17", + "lng": "-4", + "name": "Mali" + }, + { + "iso": "MT", + "lat": "35.8333", + "lng": "14.5833", + "name": "Malta" + }, + { + "iso": "MH", + "lat": "9", + "lng": "168", + "name": "Marshall Islands" + }, + { + "iso": "MQ", + "lat": "14.6667", + "lng": "-61", + "name": "Martinique" + }, + { + "iso": "MR", + "lat": "20", + "lng": "-12", + "name": "Mauritania" + }, + { + "iso": "MU", + "lat": "-20.2833", + "lng": "57.55", + "name": "Mauritius" + }, + { + "iso": "YT", + "lat": "-12.8333", + "lng": "45.1667", + "name": "Mayotte" + }, + { + "iso": "MX", + "lat": "23", + "lng": "-102", + "name": "Mexico" + }, + { + "iso": "Federated States of", + "lat": "583", + "lng": "6.9167" + }, + { + "iso": "Republic of", + "lat": "498", + "lng": "47" + }, + { + "iso": "MC", + "lat": "43.7333", + "lng": "7.4", + "name": "Monaco" + }, + { + "iso": "MN", + "lat": "46", + "lng": "105", + "name": "Mongolia" + }, + { + "iso": "ME", + "lat": "42", + "lng": "19", + "name": "Montenegro" + }, + { + "iso": "MS", + "lat": "16.75", + "lng": "-62.2", + "name": "Montserrat" + }, + { + "iso": "MA", + "lat": "32", + "lng": "-5", + "name": "Morocco" + }, + { + "iso": "MZ", + "lat": "-18.25", + "lng": "35", + "name": "Mozambique" + }, + { + "iso": "MM", + "lat": "22", + "lng": "98", + "name": "Myanmar" + }, + { + "iso": "NA", + "lat": "-22", + "lng": "17", + "name": "Namibia" + }, + { + "iso": "NR", + "lat": "-0.5333", + "lng": "166.9167", + "name": "Nauru" + }, + { + "iso": "NP", + "lat": "28", + "lng": "84", + "name": "Nepal" + }, + { + "iso": "AN", + "lat": "12.25", + "lng": "-68.75" + }, + { + "iso": "NL", + "lat": "52.5", + "lng": "5.75", + "name": "Netherlands" + }, + { + "iso": "NC", + "lat": "-21.5", + "lng": "165.5", + "name": "New Caledonia" + }, + { + "iso": "NZ", + "lat": "-41", + "lng": "174", + "name": "New Zealand" + }, + { + "iso": "NI", + "lat": "13", + "lng": "-85", + "name": "Nicaragua" + }, + { + "iso": "NE", + "lat": "16", + "lng": "8", + "name": "Niger" + }, + { + "iso": "NG", + "lat": "10", + "lng": "8", + "name": "Nigeria" + }, + { + "iso": "NU", + "lat": "-19.0333", + "lng": "-169.8667", + "name": "Niue" + }, + { + "iso": "NF", + "lat": "-29.0333", + "lng": "167.95", + "name": "Norfolk Island" + }, + { + "iso": "MP", + "lat": "15.2", + "lng": "145.75", + "name": "Northern Mariana Islands" + }, + { + "iso": "NO", + "lat": "62", + "lng": "10", + "name": "Norway" + }, + { + "iso": "OM", + "lat": "21", + "lng": "57", + "name": "Oman" + }, + { + "iso": "PK", + "lat": "30", + "lng": "70", + "name": "Pakistan" + }, + { + "iso": "PW", + "lat": "7.5", + "lng": "134.5", + "name": "Palau" + }, + { + "iso": "Occupied", + "lat": "275", + "lng": "32" + }, + { + "iso": "PA", + "lat": "9", + "lng": "-80", + "name": "Panama" + }, + { + "iso": "PG", + "lat": "-6", + "lng": "147", + "name": "Papua New Guinea" + }, + { + "iso": "PY", + "lat": "-23", + "lng": "-58", + "name": "Paraguay" + }, + { + "iso": "PE", + "lat": "-10", + "lng": "-76", + "name": "Peru" + }, + { + "iso": "PH", + "lat": "13", + "lng": "122", + "name": "Philippines" + }, + { + "iso": "PN", + "lat": "-24.7", + "lng": "-127.4", + "name": "Pitcairn" + }, + { + "iso": "PL", + "lat": "52", + "lng": "20", + "name": "Poland" + }, + { + "iso": "PT", + "lat": "39.5", + "lng": "-8", + "name": "Portugal" + }, + { + "iso": "PR", + "lat": "18.25", + "lng": "-66.5", + "name": "Puerto Rico" + }, + { + "iso": "QA", + "lat": "25.5", + "lng": "51.25", + "name": "Qatar" + }, + { + "iso": "RE", + "lat": "-21.1", + "lng": "55.6", + "name": "Réunion" + }, + { + "iso": "RO", + "lat": "46", + "lng": "25", + "name": "Romania" + }, + { + "iso": "RU", + "lat": "60", + "lng": "100", + "name": "Russian Federation" + }, + { + "iso": "RU", + "lat": "60", + "lng": "100", + "name": "Russian Federation" + }, + { + "iso": "RW", + "lat": "-2", + "lng": "30", + "name": "Rwanda" + }, + { + "iso": "BL", + "lat": "17.897728", + "lng": "-62.834244", + "name": "Saint Barthélemy" + }, + { + "iso": "Ascension and Tristan da Cunha", + "lat": "654", + "lng": "-15.9333" + }, + { + "iso": "KN", + "lat": "17.3333", + "lng": "-62.75", + "name": "Saint Kitts and Nevis" + }, + { + "iso": "LC", + "lat": "13.8833", + "lng": "-61.1333", + "name": "Saint Lucia" + }, + { + "iso": "MF", + "lat": "18.075278", + "lng": "-63.06", + "name": "Saint Martin (French part)" + }, + { + "iso": "PM", + "lat": "46.8333", + "lng": "-56.3333", + "name": "Saint Pierre and Miquelon" + }, + { + "iso": "VC", + "lat": "13.25", + "lng": "-61.2", + "name": "Saint Vincent and the Grenadines" + }, + { + "iso": "VC", + "lat": "13.25", + "lng": "-61.2", + "name": "Saint Vincent and the Grenadines" + }, + { + "iso": "WS", + "lat": "-13.5833", + "lng": "-172.3333", + "name": "Samoa" + }, + { + "iso": "SM", + "lat": "43.7667", + "lng": "12.4167", + "name": "San Marino" + }, + { + "iso": "ST", + "lat": "1", + "lng": "7", + "name": "Sao Tome and Principe" + }, + { + "iso": "SA", + "lat": "25", + "lng": "45", + "name": "Saudi Arabia" + }, + { + "iso": "SN", + "lat": "14", + "lng": "-14", + "name": "Senegal" + }, + { + "iso": "RS", + "lat": "44", + "lng": "21", + "name": "Serbia" + }, + { + "iso": "SC", + "lat": "-4.5833", + "lng": "55.6667", + "name": "Seychelles" + }, + { + "iso": "SL", + "lat": "8.5", + "lng": "-11.5", + "name": "Sierra Leone" + }, + { + "iso": "SG", + "lat": "1.3667", + "lng": "103.8", + "name": "Singapore" + }, + { + "iso": "SX", + "lat": "18.033333", + "lng": "-63.05", + "name": "Sint Maarten (Dutch part)" + }, + { + "iso": "SK", + "lat": "48.6667", + "lng": "19.5", + "name": "Slovakia" + }, + { + "iso": "SI", + "lat": "46", + "lng": "15", + "name": "Slovenia" + }, + { + "iso": "SB", + "lat": "-8", + "lng": "159", + "name": "Solomon Islands" + }, + { + "iso": "SO", + "lat": "10", + "lng": "49", + "name": "Somalia" + }, + { + "iso": "ZA", + "lat": "-29", + "lng": "24", + "name": "South Africa" + }, + { + "iso": "GS", + "lat": "-54.5", + "lng": "-37", + "name": "South Georgia and the South Sandwich Islands" + }, + { + "iso": "KR", + "lat": "37", + "lng": "127.5", + "name": "Korea, Republic of" + }, + { + "iso": "SS", + "lat": "8", + "lng": "30", + "name": "South Sudan" + }, + { + "iso": "ES", + "lat": "40", + "lng": "-4", + "name": "Spain" + }, + { + "iso": "LK", + "lat": "7", + "lng": "81", + "name": "Sri Lanka" + }, + { + "iso": "VC", + "lat": "13.25", + "lng": "-61.2", + "name": "Saint Vincent and the Grenadines" + }, + { + "iso": "SD", + "lat": "15", + "lng": "30", + "name": "Sudan" + }, + { + "iso": "SR", + "lat": "4", + "lng": "-56", + "name": "Suriname" + }, + { + "iso": "SJ", + "lat": "78", + "lng": "20", + "name": "Svalbard and Jan Mayen" + }, + { + "iso": "SZ", + "lat": "-26.5", + "lng": "31.5", + "name": "Swaziland" + }, + { + "iso": "SE", + "lat": "62", + "lng": "15", + "name": "Sweden" + }, + { + "iso": "CH", + "lat": "47", + "lng": "8", + "name": "Switzerland" + }, + { + "iso": "SY", + "lat": "35", + "lng": "38", + "name": "Syrian Arab Republic" + }, + { + "iso": "TW", + "lat": "23.5", + "lng": "121", + "name": "Taiwan, Province of China" + }, + { + "iso": "TJ", + "lat": "39", + "lng": "71", + "name": "Tajikistan" + }, + { + "iso": "United Republic of", + "lat": "834", + "lng": "-6" + }, + { + "iso": "TH", + "lat": "15", + "lng": "100", + "name": "Thailand" + }, + { + "iso": "TL", + "lat": "-8.55", + "lng": "125.5167", + "name": "Timor-Leste" + }, + { + "iso": "TG", + "lat": "8", + "lng": "1.1667", + "name": "Togo" + }, + { + "iso": "TK", + "lat": "-9", + "lng": "-172", + "name": "Tokelau" + }, + { + "iso": "TO", + "lat": "-20", + "lng": "-175", + "name": "Tonga" + }, + { + "iso": "TT", + "lat": "11", + "lng": "-61", + "name": "Trinidad and Tobago" + }, + { + "iso": "TN", + "lat": "34", + "lng": "9", + "name": "Tunisia" + }, + { + "iso": "TR", + "lat": "39", + "lng": "35", + "name": "Turkey" + }, + { + "iso": "TM", + "lat": "40", + "lng": "60", + "name": "Turkmenistan" + }, + { + "iso": "TC", + "lat": "21.75", + "lng": "-71.5833", + "name": "Turks and Caicos Islands" + }, + { + "iso": "TV", + "lat": "-8", + "lng": "178", + "name": "Tuvalu" + }, + { + "iso": "UG", + "lat": "1", + "lng": "32", + "name": "Uganda" + }, + { + "iso": "UA", + "lat": "49", + "lng": "32", + "name": "Ukraine" + }, + { + "iso": "AE", + "lat": "24", + "lng": "54", + "name": "United Arab Emirates" + }, + { + "iso": "GB", + "lat": "54", + "lng": "-2", + "name": "United Kingdom" + }, + { + "iso": "UM", + "lat": "19.2833", + "lng": "166.6", + "name": "United States Minor Outlying Islands" + }, + { + "iso": "US", + "lat": "38", + "lng": "-97", + "name": "United States" + }, + { + "iso": "UY", + "lat": "-33", + "lng": "-56", + "name": "Uruguay" + }, + { + "iso": "UZ", + "lat": "41", + "lng": "64", + "name": "Uzbekistan" + }, + { + "iso": "VU", + "lat": "-16", + "lng": "167", + "name": "Vanuatu" + }, + { + "iso": "Bolivarian Republic of", + "lat": "862", + "lng": "8" + }, + { + "iso": "VE", + "lat": "8", + "lng": "-66", + "name": "Venezuela, Bolivarian Republic of" + }, + { + "iso": "VN", + "lat": "16", + "lng": "106", + "name": "Viet Nam" + }, + { + "iso": "VN", + "lat": "16", + "lng": "106", + "name": "Viet Nam" + }, + { + "iso": "British", + "lat": "92", + "lng": "18.5" + }, + { + "iso": "U.S.", + "lat": "850", + "lng": "18.3333" + }, + { + "iso": "WF", + "lat": "-13.3", + "lng": "-176.2", + "name": "Wallis and Futuna" + }, + { + "iso": "EH", + "lat": "24.5", + "lng": "-13", + "name": "Western Sahara" + }, + { + "iso": "YE", + "lat": "15", + "lng": "48", + "name": "Yemen" + }, + { + "iso": "ZM", + "lat": "-15", + "lng": "30", + "name": "Zambia" + }, + { + "iso": "ZW", + "lat": "-20", + "lng": "30", + "name": "Zimbabwe" + } +] \ No newline at end of file diff --git a/src/components/Peers/types.ts b/src/components/Peers/types.ts new file mode 100644 index 0000000..a3e1bea --- /dev/null +++ b/src/components/Peers/types.ts @@ -0,0 +1,4 @@ +export type PeerPin = { + lat: number; + lng: number; +}; \ No newline at end of file diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 2484188..3441411 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -17,6 +17,7 @@ import { Route as DashboardIndexImport } from './routes/dashboard/index' import { Route as DashboardSettingsImport } from './routes/dashboard/settings' import { Route as DashboardRequestsImport } from './routes/dashboard/requests' import { Route as DashboardPurchasesImport } from './routes/dashboard/purchases' +import { Route as DashboardPeersImport } from './routes/dashboard/peers' import { Route as DashboardHelpImport } from './routes/dashboard/help' import { Route as DashboardFavoritesImport } from './routes/dashboard/favorites' import { Route as DashboardDisclaimerImport } from './routes/dashboard/disclaimer' @@ -55,6 +56,11 @@ const DashboardPurchasesRoute = DashboardPurchasesImport.update({ getParentRoute: () => DashboardRoute, } as any) +const DashboardPeersRoute = DashboardPeersImport.update({ + path: '/peers', + getParentRoute: () => DashboardRoute, +} as any) + const DashboardHelpRoute = DashboardHelpImport.update({ path: '/help', getParentRoute: () => DashboardRoute, @@ -133,6 +139,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardHelpImport parentRoute: typeof DashboardImport } + '/dashboard/peers': { + id: '/dashboard/peers' + path: '/peers' + fullPath: '/dashboard/peers' + preLoaderRoute: typeof DashboardPeersImport + parentRoute: typeof DashboardImport + } '/dashboard/purchases': { id: '/dashboard/purchases' path: '/purchases' @@ -172,6 +185,7 @@ interface DashboardRouteChildren { DashboardDisclaimerRoute: typeof DashboardDisclaimerRoute DashboardFavoritesRoute: typeof DashboardFavoritesRoute DashboardHelpRoute: typeof DashboardHelpRoute + DashboardPeersRoute: typeof DashboardPeersRoute DashboardPurchasesRoute: typeof DashboardPurchasesRoute DashboardRequestsRoute: typeof DashboardRequestsRoute DashboardSettingsRoute: typeof DashboardSettingsRoute @@ -184,6 +198,7 @@ const DashboardRouteChildren: DashboardRouteChildren = { DashboardDisclaimerRoute: DashboardDisclaimerRoute, DashboardFavoritesRoute: DashboardFavoritesRoute, DashboardHelpRoute: DashboardHelpRoute, + DashboardPeersRoute: DashboardPeersRoute, DashboardPurchasesRoute: DashboardPurchasesRoute, DashboardRequestsRoute: DashboardRequestsRoute, DashboardSettingsRoute: DashboardSettingsRoute, @@ -202,6 +217,7 @@ export interface FileRoutesByFullPath { '/dashboard/disclaimer': typeof DashboardDisclaimerRoute '/dashboard/favorites': typeof DashboardFavoritesRoute '/dashboard/help': typeof DashboardHelpRoute + '/dashboard/peers': typeof DashboardPeersRoute '/dashboard/purchases': typeof DashboardPurchasesRoute '/dashboard/requests': typeof DashboardRequestsRoute '/dashboard/settings': typeof DashboardSettingsRoute @@ -215,6 +231,7 @@ export interface FileRoutesByTo { '/dashboard/disclaimer': typeof DashboardDisclaimerRoute '/dashboard/favorites': typeof DashboardFavoritesRoute '/dashboard/help': typeof DashboardHelpRoute + '/dashboard/peers': typeof DashboardPeersRoute '/dashboard/purchases': typeof DashboardPurchasesRoute '/dashboard/requests': typeof DashboardRequestsRoute '/dashboard/settings': typeof DashboardSettingsRoute @@ -230,6 +247,7 @@ export interface FileRoutesById { '/dashboard/disclaimer': typeof DashboardDisclaimerRoute '/dashboard/favorites': typeof DashboardFavoritesRoute '/dashboard/help': typeof DashboardHelpRoute + '/dashboard/peers': typeof DashboardPeersRoute '/dashboard/purchases': typeof DashboardPurchasesRoute '/dashboard/requests': typeof DashboardRequestsRoute '/dashboard/settings': typeof DashboardSettingsRoute @@ -246,6 +264,7 @@ export interface FileRouteTypes { | '/dashboard/disclaimer' | '/dashboard/favorites' | '/dashboard/help' + | '/dashboard/peers' | '/dashboard/purchases' | '/dashboard/requests' | '/dashboard/settings' @@ -258,6 +277,7 @@ export interface FileRouteTypes { | '/dashboard/disclaimer' | '/dashboard/favorites' | '/dashboard/help' + | '/dashboard/peers' | '/dashboard/purchases' | '/dashboard/requests' | '/dashboard/settings' @@ -271,6 +291,7 @@ export interface FileRouteTypes { | '/dashboard/disclaimer' | '/dashboard/favorites' | '/dashboard/help' + | '/dashboard/peers' | '/dashboard/purchases' | '/dashboard/requests' | '/dashboard/settings' @@ -315,6 +336,7 @@ export const routeTree = rootRoute "/dashboard/disclaimer", "/dashboard/favorites", "/dashboard/help", + "/dashboard/peers", "/dashboard/purchases", "/dashboard/requests", "/dashboard/settings", @@ -341,6 +363,10 @@ export const routeTree = rootRoute "filePath": "dashboard/help.tsx", "parent": "/dashboard" }, + "/dashboard/peers": { + "filePath": "dashboard/peers.tsx", + "parent": "/dashboard" + }, "/dashboard/purchases": { "filePath": "dashboard/purchases.tsx", "parent": "/dashboard" diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx index c50f33a..fea649d 100644 --- a/src/routes/dashboard.tsx +++ b/src/routes/dashboard.tsx @@ -12,6 +12,7 @@ import { Settings, HelpCircle, TriangleAlert, + Earth, } from "lucide-react"; import { ICON_SIZE } from "../utils/constants"; import { NodeIndicator } from "../components/NodeIndicator/NodeIndicator"; @@ -87,6 +88,15 @@ const Layout = () => { ), }, + { + type: "menu-item", + Component: (p: MenuItemComponentProps) => ( + + + Peers + + ), + }, { type: "menu-item", Component: (p: MenuItemComponentProps) => ( diff --git a/src/routes/dashboard/peers.css b/src/routes/dashboard/peers.css new file mode 100644 index 0000000..7f308ee --- /dev/null +++ b/src/routes/dashboard/peers.css @@ -0,0 +1,41 @@ +.peers-map { + max-width: 1000px; + width: 100%; +} + +.peers-table { + margin-top: 1rem; + width: calc(100% - 4rem); + max-width: calc(1000px - 4rem); +} + +.peers { + display: flex; + flex-direction: column; + align-items: center; + padding-bottom: 4rem; + padding-left: 2rem; + padding-right: 2rem; +} + +.peers circle[fill="#d6ff79"] { + /* fill: yellow; */ + animation: dash 3s linear infinite; + stroke: white; + stroke-width: 0.6px; + stroke-dasharray: 0.3; +} + +@keyframes dash { + from { + stroke-dashoffset: 2; + } + to { + stroke-dashoffset: 0; + } +} +@keyframes circleAn { + to { + /* stroke-dashoffset: 100px; */ + } +} diff --git a/src/routes/dashboard/peers.tsx b/src/routes/dashboard/peers.tsx new file mode 100644 index 0000000..6682e60 --- /dev/null +++ b/src/routes/dashboard/peers.tsx @@ -0,0 +1,102 @@ +import { Cell, Row, Table } from "@codex-storage/marketplace-ui-components"; +import { createFileRoute } from "@tanstack/react-router"; +import { getMapJSON } from "dotted-map"; +import DottedMap from "dotted-map/without-countries"; +import { Promises } from "../../utils/promises"; +import { useQuery } from "@tanstack/react-query"; +import { PeerCountryCell } from "../../components/Peers/PeerCountryCell"; +import { useCallback, useState } from "react"; +import { PeerPin } from "../../components/Peers/types"; +import "./peers.css"; +import { CodexSdk } from "../../sdk/codex"; + +// This function accepts the same arguments as DottedMap in the example above. +const mapJsonString = getMapJSON({ height: 60, grid: "diagonal" }); + +export const Route = createFileRoute("/dashboard/peers")({ + component: () => { + const [pins, setPins] = useState<[PeerPin, number][]>([]); + const { data } = useQuery({ + queryFn: () => + CodexSdk.debug.info().then((s) => Promises.rejectOnError(s)), + queryKey: ["debug"], + + // No need to retry because if the connection to the node + // is back again, all the queries will be invalidated. + retry: false, + + // The client node should be local, so display the cache value while + // making a background request looks good. + staleTime: 0, + + // Refreshing when focus returns can be useful if a user comes back + // to the UI after performing an operation in the terminal. + refetchOnWindowFocus: true, + + // Throw the error to the error boundary + throwOnError: true, + }); + + const onPinAdd = useCallback((pin: PeerPin) => { + setPins((val) => { + const [, quantity = 0] = + val.find(([p]) => p.lat === pin.lat && p.lng == pin.lng) || []; + return [...val, [pin, quantity + 1]]; + }); + }, []); + + // It’s safe to re-create the map at each render, because of the + // pre-computation it’s super fast ⚡️ + const map = new DottedMap({ map: JSON.parse(mapJsonString) }); + + pins.map(([pin, quantity]) => + map.addPin({ + lat: pin.lat, + lng: pin.lng, + svgOptions: { color: "#d6ff79", radius: 0.8 * quantity }, + }) + ); + + const svgMap = map.getSVG({ + radius: 0.42, + color: "#423B38", + shape: "circle", + backgroundColor: "#020300", + }); + + const headers = ["Country", "PeerId", "Active"]; + + const rows = + ((data as any)?.table?.nodes || []).map((node: any) => ( + , + {node.peerId}, + + {node.seen ? ( +
+ ) : ( +
+ )} +
, + ]}>
+ )) || []; + + return ( +
+ {/* */} + +
+ + + + ); + }, +});