web


git clone https://radroots.dev/git/web.git
Log | Files | Refs | Submodules | README | LICENSE

commit 2d179cd8c530d8411d2f64fb36e20349d4612b3e
parent 53d33f1f0fa05943c57228af60e19bbc496cffb8
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu,  3 Oct 2024 14:11:38 +0000

Add `/map/choose-location` route, remove (map) layout and move map routes into (app), move nostr subscribers from root layout to (app) layout, add nostr ndk configured subscriber, edit trade products add route adding choose from map option for location gcs model

Diffstat:
Msrc/lib/components/map_control_full.svelte | 12++++++++++--
Msrc/lib/utils/location_gcs.ts | 15++++++---------
Msrc/routes/(app)/+layout.svelte | 154++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Asrc/routes/(app)/map/+page.svelte | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/routes/(app)/map/choose-location/+page.svelte | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 13+++++++++++++
Dsrc/routes/(map)/map/+page.svelte | 88-------------------------------------------------------------------------------
Msrc/routes/+layout.svelte | 134-------------------------------------------------------------------------------
8 files changed, 397 insertions(+), 234 deletions(-)

diff --git a/src/lib/components/map_control_full.svelte b/src/lib/components/map_control_full.svelte @@ -1,7 +1,15 @@ <script lang="ts"> - import { Glyph, app_layout, route } from "@radroots/svelte-lib"; + import { + Glyph, + app_layout, + type CallbackPromise, + } from "@radroots/svelte-lib"; let el_zoom: HTMLElement | null; + + export let basis: { + callback: CallbackPromise; + }; </script> <div @@ -11,7 +19,7 @@ <button class={`flex flex-row h-8 w-8 justify-center items-center rounded-2xl bg-layer-1-surface`} on:click={async () => { - await route(`/`); + await basis.callback(); }} > <Glyph diff --git a/src/lib/utils/location_gcs.ts b/src/lib/utils/location_gcs.ts @@ -25,16 +25,13 @@ export const location_gcs_add = async (): Promise<boolean> => { lat, lng, ); - const fields = { - lat: lat.toString(), - lng: lng.toString(), - geohash, - label: loc_gcs_label, - }; const exe_res = - await lc.db.location_gcs_add( - fields, - ); + await lc.db.location_gcs_add({ + lat: lat.toString(), + lng: lng.toString(), + geohash, + label: loc_gcs_label, + }); console.log(`exe_res `, exe_res) if ( typeof exe_res !== `string` && diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -1,5 +1,157 @@ <script lang="ts"> - import { LayoutWindow } from "@radroots/svelte-lib"; + import { lc } from "$lib/client"; + import { _conf } from "$lib/conf"; + import { app_nostr_key } from "$lib/stores"; + import { + type NostrRelayFormFields, + parse_nostr_relay_form_keys, + } from "@radroots/models"; + import { + LayoutWindow, + ndk, + ndk_init, + ndk_user, + nostr_ndk_configured, + nostr_relays_connected, + nostr_relays_poll_documents, + nostr_relays_poll_documents_count, + } from "@radroots/svelte-lib"; + import { parse_nostr_relay_information_document_fields } from "@radroots/utils"; + + app_nostr_key.subscribe(async (_app_nostr_key) => { + try { + if (!_app_nostr_key) return; + const secret_key = await lc.keystore.get( + _conf.kv.nostr_key(_app_nostr_key), + ); + if (!secret_key) { + alert(`!secret_key`); //@todo + return; + } + const nostr_relays = await lc.db.nostr_relay_get({ + list: ["all"], + }); + if (typeof nostr_relays === `string`) { + alert(nostr_relays); //@todo + return; + } + for (const { url } of nostr_relays) $ndk.addExplicitRelay(url); + await $ndk.connect(); + const ndk_user = await ndk_init({ + $ndk, + secret_key, + }); + if (!ndk_user) { + nostr_ndk_configured.set(false); + return; + } + $ndk_user = ndk_user; + $ndk_user.ndk = $ndk; + nostr_ndk_configured.set(true); + } catch (e) { + console.log(`(app_nostr_key) error `, e); + } + }); + + nostr_ndk_configured.subscribe(async (_nostr_ndk_configured) => { + try { + if (!_nostr_ndk_configured) return; + console.log(`(nostr_ndk_configured) success`); + nostr_relays_poll_documents.set(true); + } catch (e) { + console.log(`(error) nostr_ndk_configured`, e); + } + }); + + nostr_relays_poll_documents.subscribe( + async (_nostr_relays_poll_documents) => { + try { + if (!_nostr_relays_poll_documents) return; + await fetch_relay_documents(); + } catch (e) { + console.log(`(error) nostr_relays_poll_documents`, e); + } + }, + ); + + nostr_relays_connected.subscribe(async (_nostr_relays_connected) => { + try { + if (!_nostr_relays_connected.length) return; + else if ($nostr_ndk_configured) { + console.log(`nostr sync...`); + } + } catch (e) { + console.log(`(error) nostr_relays_connected `, e); + } + }); + + const fetch_relay_documents = async (): Promise<void> => { + try { + if ( + $nostr_relays_poll_documents_count >= + _conf.nostr.relay_polling_count_max + ) + return; + nostr_relays_poll_documents_count.set( + $nostr_relays_poll_documents_count + 1, + ); + const nostr_relays = await lc.db.nostr_relay_get({ + list: ["on_key", { public_key: $app_nostr_key }], + }); + if (typeof nostr_relays === `string`) throw new Error(); + + const unconnected_relays = nostr_relays.filter( + (i) => !$nostr_relays_connected.includes(i.id), + ); + if (unconnected_relays.length === 0) { + nostr_relays_poll_documents.set(false); + return; + } + + for (const nostr_relay of unconnected_relays) { + const res = await lc.http.fetch({ + url: nostr_relay.url.replace(`ws://`, `http://`), + headers: { + Accept: "application/nostr+json", + }, + }); + if (typeof res === `string`) continue; + else if (res.status === 200 && res.data) { + const doc = parse_nostr_relay_information_document_fields( + res.data, + ); + if (!doc) continue; + const fields: Partial<NostrRelayFormFields> = {}; + for (const [k, v] of Object.entries(doc)) { + const field_k = parse_nostr_relay_form_keys(k); + if (field_k) fields[field_k] = v; + } + if (Object.keys(fields).length < 1) continue; + await lc.db.nostr_relay_update({ + on: { + url: nostr_relay.url, + }, + fields, + }); + nostr_relays_connected.set( + Array.from( + new Set([ + ...$nostr_relays_connected, + nostr_relay.id, + ]), + ), + ); + } + } + + setTimeout( + fetch_relay_documents, + _conf.delay.nostr_relay_poll_document, + ); + } catch (e) { + console.log(`(error) fetch_relay_documents `, e); + } + }; </script> <LayoutWindow> diff --git a/src/routes/(app)/map/+page.svelte b/src/routes/(app)/map/+page.svelte @@ -0,0 +1,94 @@ +<script lang="ts"> + import { lc } from "$lib/client"; + import MapControlFull from "$lib/components/map_control_full.svelte"; + import { _conf } from "$lib/conf"; + import { app_thc } from "$lib/stores"; + import { Fill, LoadingView, route, sleep } from "@radroots/svelte-lib"; + import { MapLibre, Marker, Popup } from "@radroots/svelte-maplibre"; + import { type NumberTuple } from "@radroots/utils"; + import { onMount } from "svelte"; + + let loading_layout = true; + let map_coords: NumberTuple | undefined = undefined; + + onMount(async () => { + try { + const loc = await lc.geo.current(); + if (loc && typeof loc !== `string`) { + map_coords = [loc.lng, loc.lat]; + } + await sleep(_conf.delay.load); + } catch (e) { + console.log(`e `, e); + } finally { + loading_layout = false; + } + }); +</script> + +{#if map_coords} + <MapLibre + center={map_coords} + zoom={10} + class={`map-full ${loading_layout ? `hidden` : ``}`} + style={_conf.map.styles.base[$app_thc]} + > + <Marker lngLat={map_coords}> + <div class="flex flex-row p-1"> + <div + class={`flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} + > + <div + class={`flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} + > + <Fill /> + </div> + </div> + </div> + <Popup offset={_conf.map.popup.dot.offset}> + <button + class={`flex flex-row justify-center items-center transition-all`} + on:click={async () => {}} + > + <div + class={`flex flex-col w-fit px-2 py-1 gap-2 justify-start items-start`} + > + <div class={`flex flex-row w-full justify-start items-center`}> + <p class={`font-mono font-[400] text-layer-2-glyph`}> + {`Marker location:`} + </p> + </div> + <div + class={`flex flex-row w-full gap-2 justify-start items-center`} + > + <p class={`font-mono font-[400] text-layer-2-glyph`}> + {map_coords[0]} + </p> + <p class={`font-mono font-[400] text-layer-2-glyph`}> + {map_coords[1]} + </p> + </div> + </div> + </button> + </Popup> + </Marker> + </MapLibre> +{/if} +{#if loading_layout} + <LoadingView /> +{:else} + <MapControlFull + basis={{ + callback: async () => { + await route(`/`); + }, + }} + /> +{/if} + +<style> + :global(.map-full) { + height: 100vh; + width: 100vh; + } +</style> diff --git a/src/routes/(app)/map/choose-location/+page.svelte b/src/routes/(app)/map/choose-location/+page.svelte @@ -0,0 +1,121 @@ +<script lang="ts"> + import { lc } from "$lib/client"; + import MapControlFull from "$lib/components/map_control_full.svelte"; + import { _conf } from "$lib/conf"; + import { app_thc } from "$lib/stores"; + import { + Fill, + Glyph, + LoadingView, + route, + sleep, + } from "@radroots/svelte-lib"; + import { MapLibre, Marker } from "@radroots/svelte-maplibre"; + import { + location_geohash, + type GeolocationCoordinates, + } from "@radroots/utils"; + import { onMount } from "svelte"; + + let loading_layout = true; + let map_coords: GeolocationCoordinates | undefined = undefined; + + onMount(async () => { + try { + const loc = await lc.geo.current(); + if (loc && typeof loc !== `string`) { + map_coords = loc; + } else { + map_coords = { + lat: 0, + lng: 0, + }; + } + await sleep(_conf.delay.load); + } catch (e) { + console.log(`e `, e); + } finally { + loading_layout = false; + } + }); + + $: { + console.log(`map_coords `, map_coords); + } +</script> + +{#if map_coords} + <MapLibre + center={map_coords} + zoom={10} + class={`map-full ${loading_layout ? `hidden` : ``}`} + style={_conf.map.styles.base[$app_thc]} + > + <Marker bind:lngLat={map_coords} draggable> + <div class="relative flex flex-col w-4 items-center justify-center"> + <div + class={`absolute top-[3px] left-[3px] flex flex-row h-[10px] w-[10px] bg-red-100/30 rounded-full justify-start items-center`} + > + <Fill /> + </div> + <Glyph + basis={{ + classes: `text-red-700`, + key: `map-pin-simple`, + weight: `fill`, + dim: `xl`, + }} + /> + </div> + </Marker> + </MapLibre> +{/if} +{#if loading_layout} + <LoadingView /> +{:else} + <MapControlFull + basis={{ + callback: async () => { + if (!map_coords) return; //@todo + + const location_gcs_label = await lc.dialog.prompt({ + title: `Geolocation Label`, + message: `What is the name of the location.`, + input_placeholder: `Enter location name`, + }); + if (location_gcs_label === false) return; + else if (!location_gcs_label) { + await lc.dialog.alert(`A location name is required.`); + return; + } + + const { lat, lng } = map_coords; + const new_location_gcs = await lc.db.location_gcs_add({ + label: location_gcs_label, + geohash: location_geohash(lat, lng), + lat: lat.toString(), + lng: lng.toString(), + }); + + if (typeof new_location_gcs === `string`) { + //@todo + return; + } else if (Array.isArray(new_location_gcs)) { + //@todo + return; + } + + await route(`/models/trade-product/add`, [ + [`id`, new_location_gcs.id], + ]); + }, + }} + /> +{/if} + +<style> + :global(.map-full) { + height: 100vh; + width: 100vh; + } +</style> diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,6 +1,7 @@ <!-- svelte-ignore a11y-no-noninteractive-tabindex --> <script lang="ts"> + import { goto } from "$app/navigation"; import { lc } from "$lib/client"; import { location_gcs_add } from "$lib/utils/location_gcs"; import { @@ -306,6 +307,10 @@ value: `add-new`, label: `${$t(`common.add_current_location`)}`, }, + { + value: `add-map`, + label: `${$t(`common.add_map_location`)}`, + }, ] : [ { @@ -318,11 +323,19 @@ value: `add-new`, label: `${$t(`common.add_current_location`)}`, }, + { + value: `add-map`, + label: `${$t(`common.add_map_location`)}`, + }, ], callback: async (val) => { if (val === `add-new`) { sel_location_gcs_id = ``; await add_model_location_gcs(); + } else if (val === `add-map`) { + sel_location_gcs_id = ``; + console.log(`add map location`); + await goto(`/map/choose-location`); } }, }} diff --git a/src/routes/(map)/map/+page.svelte b/src/routes/(map)/map/+page.svelte @@ -1,88 +0,0 @@ -<script lang="ts"> - import { lc } from "$lib/client"; - import MapControlFull from "$lib/components/map_control_full.svelte"; - import { _conf } from "$lib/conf"; - import { app_thc } from "$lib/stores"; - import { Fill, LoadingView, sleep } from "@radroots/svelte-lib"; - import { MapLibre, Marker, Popup } from "@radroots/svelte-maplibre"; - import { type NumberTuple } from "@radroots/utils"; - import { onMount } from "svelte"; - - let loading_layout = true; - let map_coords: NumberTuple | undefined = undefined; - - onMount(async () => { - try { - const loc = await lc.geo.current(); - if (loc && typeof loc !== `string`) { - map_coords = [loc.lng, loc.lat]; - } - await sleep(_conf.delay.load); - } catch (e) { - console.log(`e `, e); - } finally { - loading_layout = false; - } - }); -</script> - -{#if map_coords} - <MapLibre - center={map_coords} - zoom={10} - class={`map-full ${loading_layout ? `hidden` : ``}`} - style={_conf.map.styles.base[$app_thc]} - > - <Marker lngLat={map_coords}> - <div class="flex flex-row p-1"> - <div - class={`flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} - > - <div - class={`flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} - > - <Fill /> - </div> - </div> - </div> - <Popup offset={_conf.map.popup.dot.offset}> - <button - class={`flex flex-row justify-center items-center transition-all`} - on:click={async () => {}} - > - <div - class={`flex flex-col w-fit px-2 py-1 gap-2 justify-start items-start`} - > - <div class={`flex flex-row w-full justify-start items-center`}> - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`Marker location:`} - </p> - </div> - <div - class={`flex flex-row w-full gap-2 justify-start items-center`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {map_coords[0]} - </p> - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {map_coords[1]} - </p> - </div> - </div> - </button> - </Popup> - </Marker> - </MapLibre> -{/if} -{#if loading_layout} - <LoadingView /> -{:else} - <MapControlFull /> -{/if} - -<style> - :global(.map-full) { - height: 100vh; - width: 100vh; - } -</style> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -10,28 +10,17 @@ app_thc, } from "$lib/stores"; import { - type NostrRelayFormFields, - parse_nostr_relay_form_keys, - } from "@radroots/models"; - import { app_config, app_notify, app_render, AppConfig, CssStatic, CssStyles, - ndk, - ndk_init, - ndk_user, - nostr_relays_connected, - nostr_relays_poll_documents, - nostr_relays_poll_documents_count, route, sleep, theme_set, } from "@radroots/svelte-lib"; import { parse_color_mode, parse_theme_key } from "@radroots/theme"; - import { parse_nostr_relay_information_document_fields } from "@radroots/utils"; import "../app.css"; let render_pwa = browser && lc.platform === `web`; @@ -119,50 +108,6 @@ } }); - app_nostr_key.subscribe(async (_app_nostr_key) => { - try { - if (!_app_nostr_key) return; - - const secret_key = await lc.keystore.get( - _conf.kv.nostr_key(_app_nostr_key), - ); - if (!secret_key) { - alert(`!secret_key`); //@todo - return; - } - - const nostr_relays = await lc.db.nostr_relay_get({ - list: ["all"], - }); - if (typeof nostr_relays === `string`) { - alert(nostr_relays); //@todo - return; - } - - for (const { url } of nostr_relays) $ndk.addExplicitRelay(url); - - await $ndk.connect().then(() => { - console.log(`(ndk) connected`); - }); - - const ndk_user = await ndk_init({ - $ndk, - secret_key, - }); - if (!ndk_user) { - alert(`!ndk_user`); //@todo - return; - } - - $ndk_user = ndk_user; - $ndk_user.ndk = $ndk; - console.log(`(ndk) initialized`); - nostr_relays_poll_documents.set(true); - } catch (e) { - console.log(`(app_nostr_key) error `, e); - } - }); - app_notify.subscribe(async (_app_notify) => { if (!_app_notify) return; route(`/`); @@ -170,85 +115,6 @@ lc.dialog.alert(_app_notify); app_notify.set(``); }); - - nostr_relays_poll_documents.subscribe( - async (_nostr_relays_poll_documents) => { - try { - if (!_nostr_relays_poll_documents) return; - await fetch_relay_documents(); - } catch (e) { - console.log(`(error) nostr_relays_poll_documents`, e); - } - }, - ); - - const fetch_relay_documents = async (): Promise<void> => { - try { - if ( - $nostr_relays_poll_documents_count >= - _conf.nostr.relay_polling_count_max - ) - return; - nostr_relays_poll_documents_count.set( - $nostr_relays_poll_documents_count + 1, - ); - const nostr_relays = await lc.db.nostr_relay_get({ - list: ["on_key", { public_key: $app_nostr_key }], - }); - if (typeof nostr_relays === `string`) throw new Error(); - - const unconnected_relays = nostr_relays.filter( - (i) => !$nostr_relays_connected.includes(i.id), - ); - if (unconnected_relays.length === 0) { - nostr_relays_poll_documents.set(false); - return; - } - - for (const nostr_relay of unconnected_relays) { - const res = await lc.http.fetch({ - url: nostr_relay.url.replace(`ws://`, `http://`), - headers: { - Accept: "application/nostr+json", - }, - }); - if (typeof res === `string`) continue; - else if (res.status === 200 && res.data) { - const doc = parse_nostr_relay_information_document_fields( - res.data, - ); - if (!doc) continue; - const fields: Partial<NostrRelayFormFields> = {}; - for (const [k, v] of Object.entries(doc)) { - const field_k = parse_nostr_relay_form_keys(k); - if (field_k) fields[field_k] = v; - } - if (Object.keys(fields).length < 1) continue; - await lc.db.nostr_relay_update({ - on: { - url: nostr_relay.url, - }, - fields, - }); - nostr_relays_connected.set( - Array.from( - new Set([ - ...$nostr_relays_connected, - nostr_relay.id, - ]), - ), - ); - } - } - - setTimeout( - fetch_relay_documents, - _conf.delay.nostr_relay_poll_document, - ); - } catch (e) { - console.log(`(error) fetch_relay_documents `, e); - } - }; </script> <svelte:head>