feat: render NIP-107 data
This commit is contained in:
parent
470e5b31ce
commit
14c8c9a080
@ -1 +1,4 @@
|
|||||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||||
|
npmScopes:
|
||||||
|
"here":
|
||||||
|
npmRegistryServer: "https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cashu/cashu-ts": "0.6.1",
|
"@cashu/cashu-ts": "0.6.1",
|
||||||
|
"@here/maps-api-for-javascript": "^1.50.0",
|
||||||
"@lightninglabs/lnc-web": "^0.2.8-alpha",
|
"@lightninglabs/lnc-web": "^0.2.8-alpha",
|
||||||
"@noble/curves": "^1.0.0",
|
"@noble/curves": "^1.0.0",
|
||||||
"@noble/hashes": "^1.3.3",
|
"@noble/hashes": "^1.3.3",
|
||||||
@ -26,6 +27,7 @@
|
|||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"highlight.js": "^11.8.0",
|
"highlight.js": "^11.8.0",
|
||||||
|
"latlon-geohash": "^2.0.0",
|
||||||
"light-bolt11-decoder": "^2.1.0",
|
"light-bolt11-decoder": "^2.1.0",
|
||||||
"lottie-react": "^2.4.0",
|
"lottie-react": "^2.4.0",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
@ -86,6 +88,7 @@
|
|||||||
"@formatjs/cli": "^6.1.3",
|
"@formatjs/cli": "^6.1.3",
|
||||||
"@types/config": "^3.3.3",
|
"@types/config": "^3.3.3",
|
||||||
"@types/debug": "^4.1.8",
|
"@types/debug": "^4.1.8",
|
||||||
|
"@types/latlon-geohash": "^2.0.3",
|
||||||
"@types/node": "^20.4.1",
|
"@types/node": "^20.4.1",
|
||||||
"@types/react": "^18.0.26",
|
"@types/react": "^18.0.26",
|
||||||
"@types/react-dom": "^18.0.10",
|
"@types/react-dom": "^18.0.10",
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
import { dedupe, removeUndefined } from "@snort/shared";
|
||||||
import { NostrLink, ReqFilter, RequestBuilder } from "@snort/system";
|
import { NostrLink, ReqFilter, RequestBuilder } from "@snort/system";
|
||||||
import { useRequestBuilder } from "@snort/system-react";
|
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 { 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 }) {
|
export function GenericFeed({ link }: { link: NostrLink }) {
|
||||||
|
const reqs = JSON.parse(link.id) as Array<ReqFilter>;
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
const sub = new RequestBuilder("generic");
|
const sub = new RequestBuilder("generic");
|
||||||
sub.withOptions({ leaveOpen: true });
|
sub.withOptions({ leaveOpen: true });
|
||||||
const reqs = JSON.parse(link.id) as Array<ReqFilter>;
|
|
||||||
reqs.forEach(a => {
|
reqs.forEach(a => {
|
||||||
const f = sub.withBareFilter(a);
|
const f = sub.withBareFilter(a);
|
||||||
link.relays?.forEach(r => f.relay(r));
|
link.relays?.forEach(r => f.relay(r));
|
||||||
@ -18,13 +23,50 @@ export function GenericFeed({ link }: { link: NostrLink }) {
|
|||||||
|
|
||||||
const evs = useRequestBuilder(sub);
|
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 (
|
||||||
|
<div className="p flex flex-col gap-2">
|
||||||
|
{geoTags.length > 0 && (
|
||||||
|
<Suspense>
|
||||||
|
<LazyMap
|
||||||
|
zoom={2}
|
||||||
|
center={{ lat: 30, lng: 0 }}
|
||||||
|
markers={geoTags.map(a => {
|
||||||
|
const pos = Geohash.decode(a);
|
||||||
|
return {
|
||||||
|
lat: pos.lat,
|
||||||
|
lng: pos.lon,
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
|
<Suspense>
|
||||||
|
<LazySimpleChart
|
||||||
|
data={evs
|
||||||
|
.sort((a, b) => (a.created_at > b.created_at ? -1 : 1))
|
||||||
|
.map(a => {
|
||||||
|
return {
|
||||||
|
time: a.created_at * 1000,
|
||||||
|
...JSON.parse(a.content),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<TimelineRenderer
|
<>
|
||||||
frags={[{ events: evs, refTime: 0 }]}
|
<TimelineRenderer
|
||||||
latest={[]}
|
frags={[{ events: evs, refTime: 0 }]}
|
||||||
showLatest={() => {
|
latest={[]}
|
||||||
//nothing
|
showLatest={() => {
|
||||||
}}
|
//nothing
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
55
packages/app/src/Components/HereMap.tsx
Normal file
55
packages/app/src/Components/HereMap.tsx
Normal file
@ -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<H.Map>(null);
|
||||||
|
const platform = useRef<H.service.Platform>(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 <div style={{ width: "100%", height: "300px" }} className="rounded-xl overflow-hidden" ref={mapRef} />;
|
||||||
|
}
|
73
packages/app/src/Components/LineChart.tsx
Normal file
73
packages/app/src/Components/LineChart.tsx
Normal file
@ -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 (
|
||||||
|
<ResponsiveContainer height={250}>
|
||||||
|
<LineChart data={data}>
|
||||||
|
<XAxis
|
||||||
|
dataKey="time"
|
||||||
|
type="number"
|
||||||
|
scale="time"
|
||||||
|
domain={["dataMin", "dataMax"]}
|
||||||
|
tickFormatter={v => 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<MinMax>(
|
||||||
|
(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 (
|
||||||
|
<>
|
||||||
|
<Line dataKey={a} type="monotone" dot={false} stroke={`#${sha256(a).slice(0, 6)}`} yAxisId={a} />
|
||||||
|
<YAxis
|
||||||
|
dataKey={a}
|
||||||
|
unit={mapUnit()}
|
||||||
|
yAxisId={a}
|
||||||
|
type="number"
|
||||||
|
scale="linear"
|
||||||
|
domain={domainOf()}
|
||||||
|
tickFormatter={v => Number(v).toLocaleString()}
|
||||||
|
hide={true}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<Legend />
|
||||||
|
<Tooltip />
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
}
|
24
yarn.lock
24
yarn.lock
@ -2189,6 +2189,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@humanwhocodes/config-array@npm:^0.11.13":
|
||||||
version: 0.11.13
|
version: 0.11.13
|
||||||
resolution: "@humanwhocodes/config-array@npm:0.11.13"
|
resolution: "@humanwhocodes/config-array@npm:0.11.13"
|
||||||
@ -2925,6 +2932,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@cashu/cashu-ts": 0.6.1
|
"@cashu/cashu-ts": 0.6.1
|
||||||
"@formatjs/cli": ^6.1.3
|
"@formatjs/cli": ^6.1.3
|
||||||
|
"@here/maps-api-for-javascript": ^1.50.0
|
||||||
"@lightninglabs/lnc-web": ^0.2.8-alpha
|
"@lightninglabs/lnc-web": ^0.2.8-alpha
|
||||||
"@noble/curves": ^1.0.0
|
"@noble/curves": ^1.0.0
|
||||||
"@noble/hashes": ^1.3.3
|
"@noble/hashes": ^1.3.3
|
||||||
@ -2940,6 +2948,7 @@ __metadata:
|
|||||||
"@szhsin/react-menu": ^3.3.1
|
"@szhsin/react-menu": ^3.3.1
|
||||||
"@types/config": ^3.3.3
|
"@types/config": ^3.3.3
|
||||||
"@types/debug": ^4.1.8
|
"@types/debug": ^4.1.8
|
||||||
|
"@types/latlon-geohash": ^2.0.3
|
||||||
"@types/node": ^20.4.1
|
"@types/node": ^20.4.1
|
||||||
"@types/react": ^18.0.26
|
"@types/react": ^18.0.26
|
||||||
"@types/react-dom": ^18.0.10
|
"@types/react-dom": ^18.0.10
|
||||||
@ -2972,6 +2981,7 @@ __metadata:
|
|||||||
eventemitter3: ^5.0.1
|
eventemitter3: ^5.0.1
|
||||||
fuse.js: ^7.0.0
|
fuse.js: ^7.0.0
|
||||||
highlight.js: ^11.8.0
|
highlight.js: ^11.8.0
|
||||||
|
latlon-geohash: ^2.0.0
|
||||||
light-bolt11-decoder: ^2.1.0
|
light-bolt11-decoder: ^2.1.0
|
||||||
lottie-react: ^2.4.0
|
lottie-react: ^2.4.0
|
||||||
marked: ^9.1.0
|
marked: ^9.1.0
|
||||||
@ -3622,6 +3632,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@types/lokijs@npm:^1.5.14":
|
||||||
version: 1.5.14
|
version: 1.5.14
|
||||||
resolution: "@types/lokijs@npm:1.5.14"
|
resolution: "@types/lokijs@npm:1.5.14"
|
||||||
@ -8101,6 +8118,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"leven@npm:^3.1.0":
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
resolution: "leven@npm:3.1.0"
|
resolution: "leven@npm:3.1.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user