From 14c8c9a0804066baa80b9597b1f3313ed6495717 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 29 Jan 2024 14:38:31 +0000 Subject: [PATCH] feat: render NIP-107 data --- .yarnrc.yml | 3 + packages/app/package.json | 3 + packages/app/src/Components/Feed/Generic.tsx | 60 +++++++++++++--- packages/app/src/Components/HereMap.tsx | 55 +++++++++++++++ packages/app/src/Components/LineChart.tsx | 73 ++++++++++++++++++++ yarn.lock | 24 +++++++ 6 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/Components/HereMap.tsx create mode 100644 packages/app/src/Components/LineChart.tsx diff --git a/.yarnrc.yml b/.yarnrc.yml index 44f6a233..cc3a15d7 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,4 @@ yarnPath: .yarn/releases/yarn-3.6.3.cjs +npmScopes: + "here": + npmRegistryServer: "https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/" diff --git a/packages/app/package.json b/packages/app/package.json index c966a411..6e55de2b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -3,6 +3,7 @@ "version": "0.2.0", "dependencies": { "@cashu/cashu-ts": "0.6.1", + "@here/maps-api-for-javascript": "^1.50.0", "@lightninglabs/lnc-web": "^0.2.8-alpha", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.3", @@ -26,6 +27,7 @@ "eventemitter3": "^5.0.1", "fuse.js": "^7.0.0", "highlight.js": "^11.8.0", + "latlon-geohash": "^2.0.0", "light-bolt11-decoder": "^2.1.0", "lottie-react": "^2.4.0", "marked": "^9.1.0", @@ -86,6 +88,7 @@ "@formatjs/cli": "^6.1.3", "@types/config": "^3.3.3", "@types/debug": "^4.1.8", + "@types/latlon-geohash": "^2.0.3", "@types/node": "^20.4.1", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", diff --git a/packages/app/src/Components/Feed/Generic.tsx b/packages/app/src/Components/Feed/Generic.tsx index 61a3e78f..cf0eb5a2 100644 --- a/packages/app/src/Components/Feed/Generic.tsx +++ b/packages/app/src/Components/Feed/Generic.tsx @@ -1,14 +1,19 @@ +import { dedupe, removeUndefined } from "@snort/shared"; import { NostrLink, ReqFilter, RequestBuilder } from "@snort/system"; import { useRequestBuilder } from "@snort/system-react"; -import { useMemo } from "react"; +import Geohash from "latlon-geohash"; +import { lazy, Suspense, useMemo } from "react"; import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer"; +import { findTag } from "@/Utils"; +const LazySimpleChart = lazy(async () => await import("@/Components/LineChart")); +const LazyMap = lazy(async () => await import("@/Components/HereMap")); export function GenericFeed({ link }: { link: NostrLink }) { + const reqs = JSON.parse(link.id) as Array; const sub = useMemo(() => { const sub = new RequestBuilder("generic"); sub.withOptions({ leaveOpen: true }); - const reqs = JSON.parse(link.id) as Array; reqs.forEach(a => { const f = sub.withBareFilter(a); link.relays?.forEach(r => f.relay(r)); @@ -18,13 +23,50 @@ export function GenericFeed({ link }: { link: NostrLink }) { const evs = useRequestBuilder(sub); + const geoTags = dedupe(removeUndefined(evs.map(a => findTag(a, "g")))); + const isTempSensor = reqs[0].kinds?.includes(8001) && reqs[0].kinds.length === 1; + if (isTempSensor) { + return ( +
+ {geoTags.length > 0 && ( + + { + const pos = Geohash.decode(a); + return { + lat: pos.lat, + lng: pos.lon, + }; + })} + /> + + )} + + (a.created_at > b.created_at ? -1 : 1)) + .map(a => { + return { + time: a.created_at * 1000, + ...JSON.parse(a.content), + }; + })} + /> + +
+ ); + } return ( - { - //nothing - }} - /> + <> + { + //nothing + }} + /> + ); } diff --git a/packages/app/src/Components/HereMap.tsx b/packages/app/src/Components/HereMap.tsx new file mode 100644 index 00000000..090c9845 --- /dev/null +++ b/packages/app/src/Components/HereMap.tsx @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import H from "@here/maps-api-for-javascript"; +import { useEffect, useRef } from "react"; + +export default function HereMap(props: { + zoom?: number; + markers?: Array<{ lat: number; lng: number }>; + center?: { lat: number; lng: number }; +}) { + const mapRef = useRef(null); + const map = useRef(null); + const platform = useRef(null); + + useEffect(() => { + if (!map.current) { + //@ts-expect-error + platform.current = new H.service.Platform({ apikey: "5uZZsWJdVyMSDTjjNJNyUgKq_bKv2rVVZWAXnfmgttQ" }); + + const rasterTileService = platform.current.getRasterTileService({ + queryParams: { + style: "explore.night", + size: 512, + }, + }); + const rasterTileProvider = new H.service.rasterTile.Provider(rasterTileService); + const rasterTileLayer = new H.map.layer.TileLayer(rasterTileProvider); + + const newMap = new H.Map(mapRef.current!, rasterTileLayer, { + engineType: H.Map.EngineType.WEBGL, + pixelRatio: window.devicePixelRatio, + center: props.center ?? { lat: 0, lng: 0 }, + zoom: props.zoom ?? 2, + }); + + new H.mapevents.Behavior(new H.mapevents.MapEvents(newMap)); + //@ts-expect-error + map.current = newMap; + } + }, [props]); + + useEffect(() => { + if (map.current) { + map.current.setCenter(props.center ?? { lat: 0, lng: 0 }); + map.current.setZoom(props.zoom ?? 2); + map.current.removeObjects(map.current.getObjects()); + for (const mrk of props.markers ?? []) { + const m = new H.map.Marker(mrk); + map.current.addObject(m); + } + } + }, [map, props.center, props.zoom, props.markers]); + + // Return a div element to hold the map + return
; +} diff --git a/packages/app/src/Components/LineChart.tsx b/packages/app/src/Components/LineChart.tsx new file mode 100644 index 00000000..2df00505 --- /dev/null +++ b/packages/app/src/Components/LineChart.tsx @@ -0,0 +1,73 @@ +import { sha256 } from "@snort/shared"; +import { Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; + +export default function SensorChart({ data }: { data: Array<{ time: number; [k: string]: number }> }) { + return ( + + + new Date(Number(v)).toLocaleTimeString()} + /> + {Object.keys(data[0] ?? {}) + .filter(a => a !== "time") + .map(a => { + const mapUnit = () => { + switch (a) { + case "temperature": + return "C"; + case "humidity": + return "%"; + case "wind_direction": + return "deg"; + case "wind_speed": + return "m/s"; + case "rain": + return "mm"; + } + }; + interface MinMax { + min: number; + max: number; + } + const domainOf = () => { + const domain = data.reduce( + (acc, v) => { + if (v[a] < acc.min) { + acc.min = v[a]; + } + if (v[a] > acc.max) { + acc.max = v[a]; + } + return acc; + }, + { min: Number.MAX_SAFE_INTEGER, max: Number.MIN_SAFE_INTEGER } as MinMax, + ); + return [domain.min * 0.95, domain.max * 1.05]; + }; + + return ( + <> + + Number(v).toLocaleString()} + hide={true} + /> + + ); + })} + + + + + ); +} diff --git a/yarn.lock b/yarn.lock index f94af93e..9e91007a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2189,6 +2189,13 @@ __metadata: languageName: node linkType: hard +"@here/maps-api-for-javascript@npm:^1.50.0": + version: 1.50.0 + resolution: "@here/maps-api-for-javascript@npm:1.50.0::__archiveUrl=https%3A%2F%2Frepo.platform.here.com%2Fartifactory%2Fapi%2Fnpm%2Fmaps-api-for-javascript%2F-%2F%40here%2Fmaps-api-for-javascript-1.50.0.tgz" + checksum: d0e09651b886a69ea1504c02436bfa3e06bd1c93f2f361402bd279ccabfa43cc57fe1e31c3365c9170ff2b22cbb05ff44ead0c6cf55d118c7e279f8f7c180102 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.13": version: 0.11.13 resolution: "@humanwhocodes/config-array@npm:0.11.13" @@ -2925,6 +2932,7 @@ __metadata: dependencies: "@cashu/cashu-ts": 0.6.1 "@formatjs/cli": ^6.1.3 + "@here/maps-api-for-javascript": ^1.50.0 "@lightninglabs/lnc-web": ^0.2.8-alpha "@noble/curves": ^1.0.0 "@noble/hashes": ^1.3.3 @@ -2940,6 +2948,7 @@ __metadata: "@szhsin/react-menu": ^3.3.1 "@types/config": ^3.3.3 "@types/debug": ^4.1.8 + "@types/latlon-geohash": ^2.0.3 "@types/node": ^20.4.1 "@types/react": ^18.0.26 "@types/react-dom": ^18.0.10 @@ -2972,6 +2981,7 @@ __metadata: eventemitter3: ^5.0.1 fuse.js: ^7.0.0 highlight.js: ^11.8.0 + latlon-geohash: ^2.0.0 light-bolt11-decoder: ^2.1.0 lottie-react: ^2.4.0 marked: ^9.1.0 @@ -3622,6 +3632,13 @@ __metadata: languageName: node linkType: hard +"@types/latlon-geohash@npm:^2.0.3": + version: 2.0.3 + resolution: "@types/latlon-geohash@npm:2.0.3" + checksum: dd238573da1057bf1a458211bc65d9b05da3eb2ac11340702902c68bd85d41731789d4b1ebab1c82813a6b73ec9e57a4f0f13e4d7b7a5cb14b0d640d66901ba0 + languageName: node + linkType: hard + "@types/lokijs@npm:^1.5.14": version: 1.5.14 resolution: "@types/lokijs@npm:1.5.14" @@ -8101,6 +8118,13 @@ __metadata: languageName: node linkType: hard +"latlon-geohash@npm:^2.0.0": + version: 2.0.0 + resolution: "latlon-geohash@npm:2.0.0" + checksum: e3eae428a979326c576e3f0b5fc7505cb2a57abb7f72bb6b005d37d8a35baa90f48f009d4167c15b6470060fc826eb8ebf2a5d8d0eb35e1b6e4ff35bbc82b02b + languageName: node + linkType: hard + "leven@npm:^3.1.0": version: 3.1.0 resolution: "leven@npm:3.1.0"