app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 120c3a21a0ad7dd6cbfb9675caf3838a5d9263c6
parent f93d61e87b4c82ccaefb609c02f88772f0f02132
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu, 10 Apr 2025 23:53:35 +0000

Add/edit nostr sync and poll relay utils. Edit (app) layout subscribers. Edit `/init` client logic, styles. Edit `tangle` set tauri http permissions to all http/https addresses.

Diffstat:
Mapp/src/lib/util/index.ts | 5++++-
Aapp/src/lib/util/nostr/poll.ts | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/lib/util/nostr/sync.ts | 10++++++----
Mapp/src/routes/(app)/+layout.svelte | 29+++++++++++++++++------------
Mapp/src/routes/(cfg)/init/+page.svelte | 17++++++++---------
Mcrates/tangle/capabilities/default.json | 4++--
Mcrates/tangle/icons/icon.icns | 0
7 files changed, 142 insertions(+), 28 deletions(-)

diff --git a/app/src/lib/util/index.ts b/app/src/lib/util/index.ts @@ -5,7 +5,7 @@ import { ls } from "$lib/locale/i18n"; import { TauriClientDatabase, TauriClientDatastore, TauriClientFs, TauriClientGeolocation, TauriClientGui, TauriClientHttp, TauriClientKeys, TauriClientRadroots } from "@radroots/client"; import { Geocoder } from "@radroots/geocoder"; import { app_notify, get_store, handle_err, NostrSyncService, type NavigationRouteParamKey, type NavigationRouteParamTuple } from "@radroots/lib-app"; -import { NostrEventService, NostrKeyService } from "@radroots/nostr-util"; +import { NostrEventService, NostrKeyService, NostrRelayService } from "@radroots/nostr-util"; import { encode_route, type CallbackPromise } from "@radroots/util"; import type { NavigationRoute } from "./routes"; @@ -21,6 +21,9 @@ export const radroots = new TauriClientRadroots(PUBLIC_RADROOTS_URL); export const geoc = new Geocoder(); export const nostrkey = new NostrKeyService(); export const nostre = new NostrEventService(); +export const nostrl = new NostrRelayService(); + + export let nostrsync: NostrSyncService if (browser) nostrsync = new NostrSyncService(); diff --git a/app/src/lib/util/nostr/poll.ts b/app/src/lib/util/nostr/poll.ts @@ -0,0 +1,104 @@ +import { ls } from "$lib/locale/i18n"; +import { get_store, handle_err, ndk_user, nostr_poll_relays_stop, nostr_relays_connected } from "@radroots/lib-app"; +import { nostr_relay_parse_form_keys, type NostrRelayFormFields } from "@radroots/models"; +import { throw_err } from "@radroots/util"; +import { db, gui, http, nostrl } from ".."; + +export const nostr_poll_relays = async (): Promise<void> => { + try { + console.log(`[nostr_poll_relays] start`); + const $ls = get_store(ls); + const $ndk_user = get_store(ndk_user); + const public_key = $ndk_user?.pubkey; + if (!public_key) return void await gui.alert( + `${$ls(`error.client.nostr_poll_relays_failure`)}` + ); + + const $nostr_relays_connected = get_store(nostr_relays_connected); + + const nostr_relays = await db.nostr_relay_read_list({ + table: [`on_profile`, { public_key }], + }); + console.log(JSON.stringify(nostr_relays, null, 4), `nostr_relays`) + if (`err` in nostr_relays) return throw_err(nostr_relays.err); + const unconnected_relays = nostr_relays.results.filter( + (i) => !$nostr_relays_connected.includes(i.id), + ); + console.log(JSON.stringify(unconnected_relays, null, 4), `unconnected_relays`) + if (unconnected_relays.length === 0) return void nostr_poll_relays_stop.set(true); + + for (const nostr_relay of unconnected_relays) { + const res = await http.fetch({ + url: nostr_relay.url.replace(`ws://`, `http://`).replace(`wss://`, `https://`), + headers: { + Accept: "application/nostr+json", + }, + }); + if (`err` in res) continue; + else if (res.status === 200 && res.data) { + const relaydoc = nostrl.parse_information_document(res.data); + if (!relaydoc) continue; + const fields: Partial<NostrRelayFormFields> = {}; + for (const [k, v] of Object.entries(relaydoc)) { + const field_k = nostr_relay_parse_form_keys(k); + if (field_k) fields[field_k] = v; + } + if (Object.keys(fields).length < 1) continue; + await db.nostr_relay_update({ + filter: { + url: nostr_relay.url, + }, + fields, + }); + nostr_relays_connected.set( + Array.from( + new Set([ + ...$nostr_relays_connected, + nostr_relay.id, + ]), + ), + ); + } + } + console.log(`[nostr_poll_relays] done`); + } catch (e) { + await handle_err(e, `nostr_poll_relays`); + } +}; + + +/* +import { get_store, handle_err, ndk_user, nostr_poll_relays, nostr_poll_relays_attempts, nostr_poll_relays_attempts_max, nostr_relays_connected } from "$lib"; +import type { db } from "@nostr-dev-kit/ndk-cache-dexie"; +import { throw_err } from "@radroots/util"; + +export const nostr_fetch_relay_documents = async ( + callback: () => Promise<void> +): Promise<void> => { + try { + + if ( + $nostr_poll_relays_attempts >= + $nostr_poll_relays_attempts_max + ) { + nostr_poll_relays.set(false); + return; + } + nostr_poll_relays_attempts.set( + $nostr_poll_relays_attempts + 1, + ); + + - + if (unconnected_relays.length === 0) return void nostr_poll_relays.set(false); + + setTimeout( + async () => nostr_fetch_relay_documents(callback), + 3000 + ); + } catch (e) { + await handle_err(e, `nostr_fetch_relay_documents`); + } +}; + + +*/ +\ No newline at end of file diff --git a/app/src/lib/util/nostr/sync.ts b/app/src/lib/util/nostr/sync.ts @@ -6,9 +6,11 @@ import { err } from "../err"; export const nostr_sync = async (): Promise<void> => { try { + console.log(`[nostr_sync] start`); const $ls = get_store(ls); const $ndk_user = get_store(ndk_user); - if (!$ndk_user.pubkey) return void await gui.alert( + const public_key = $ndk_user.pubkey; + if (!public_key) return void await gui.alert( `${$ls(`error.client.nostr_sync_failure`)}` ); const $nostr_sync_prevent = get_store(nostr_sync_prevent); @@ -22,13 +24,13 @@ export const nostr_sync = async (): Promise<void> => { } return; } - console.log(`[nostr_sync] start`); const nostr_relays = await db.nostr_relay_read_list({ - table: [`on_profile`, { public_key: $ndk_user.pubkey }] + table: [`on_profile`, { public_key }] }); //@todo - + console.log(JSON.stringify(nostr_relays, null, 4), `nostr_relays`) if (`err` in nostr_relays) return throw_err(nostr_relays); if (!nostr_relays.results.length) return throw_err(err.nostr.no_relays); + await nostr_sync_metadata(); console.log(`[nostr_sync] done`); } catch (e) { diff --git a/app/src/routes/(app)/+layout.svelte b/app/src/routes/(app)/+layout.svelte @@ -8,6 +8,7 @@ ndk, ndk_user, nostr_ndk_configured, + nostr_poll_relays_retry_handler, nostr_sync_retry_handler, } from "@radroots/lib-app"; import { ndk_init } from "@radroots/nostr-util"; @@ -15,7 +16,8 @@ import { _cfg } from "$lib/config"; import { cfg_role, cfg_setup } from "$lib/store"; - import { nostr_sync_metadata } from "$lib/util/nostr/sync"; + import { nostr_poll_relays } from "$lib/util/nostr/poll"; + import { nostr_sync } from "$lib/util/nostr/sync"; import { onMount } from "svelte"; import type { LayoutProps } from "./$types"; @@ -74,27 +76,30 @@ $ndk, secret_key: keys_nostr_read.secret_key, }); - nostr_ndk_configured.set(!!ndk_user_init); if (!ndk_user_init) throw_err(`error.nostr.ndk_user_undefined`); $ndk_user = ndk_user_init; $ndk_user.ndk = $ndk; - await nostr_sync_retry_handler(nostr_sync_metadata); + nostr_ndk_configured.set(true); }; - app_notify.subscribe(async (_app_notify) => { - console.log(`_app_notify `, _app_notify); - if (!_app_notify) return; - await gui.notify_send($app_notify); + nostr_ndk_configured.subscribe(async (_sub) => { + if (!_sub) return; + await nostr_sync_retry_handler(nostr_sync); + await nostr_poll_relays_retry_handler(nostr_poll_relays); + }); + + app_notify.subscribe(async (_sub) => { + if (!_sub) return; + await gui.notify_send(_sub); app_notify.set(``); }); - cfg_role.subscribe(async (_cfg_role) => { - if (!_cfg_role) return; + cfg_role.subscribe(async (_sub) => { + if (!_sub) return; }); - cfg_setup.subscribe(async (_cfg_setup) => { - console.log(`_cfg_setup `, _cfg_setup); - if (!_cfg_setup) return; + cfg_setup.subscribe(async (_sub) => { + if (!_sub) return; }); </script> diff --git a/app/src/routes/(cfg)/init/+page.svelte b/app/src/routes/(cfg)/init/+page.svelte @@ -59,9 +59,7 @@ let cfg_profile_name_loading = $state(false); const cfg_profile_name_skip = $derived( - view.toString() === `cfg_profile` && - $carousel_index === 0 && - !cfg_profile_nip05_opt, + view.toString() === `cfg_profile` && $carousel_index === 0, ); onMount(async () => { @@ -586,7 +584,7 @@ class={`carousel-item flex flex-col h-full w-full justify-center items-center`} > <div - class={`flex flex-col h-[16rem] w-full px-4 gap-10 justify-start items-center`} + class={`flex flex-col h-[16rem] w-full px-4 gap-6 justify-start items-center`} > <p class={`font-sans font-[600] text-layer-0-glyph text-3xl`}> {`${$ls(`icu.add_*`, { value: `${$ls(`common.profile`)}` })}`} @@ -635,11 +633,11 @@ }} > <p - class={`font-sans font-[500] text-layer-0-glyph text-[14px] tracking-wide`} + class={`font-sans font-[400] text-layer-0-glyph text-[14px] tracking-wide`} > {`${$ls(`common.create`)}`} <span - class={`font-mono font-[600] tracking-tight px-[2px]`} + class={`font-mono font-[600] tracking-tight px-[3px]`} > {`@radroots`} </span> @@ -711,9 +709,10 @@ continue: { label: `${$ls(`common.continue`)}`, disabled: - // ($carousel_index === 0 && - // !cfg_profile_name_valid) || - $carousel_index === 1 && !cfg_role, + (cfg_profile_name_skip && + cfg_profile_nip05_opt && + !cfg_profile_name_valid) || + ($carousel_index === 1 && !cfg_role), callback: async () => await handle_continue(), }, back: { diff --git a/crates/tangle/capabilities/default.json b/crates/tangle/capabilities/default.json @@ -13,10 +13,10 @@ "identifier": "http:default", "allow": [ { - "url": "https://radroots.market" + "url": "https://*" }, { - "url": "https://*.radroots.market" + "url": "http://*" }, { "url": "http://127.0.0.1:*" diff --git a/crates/tangle/icons/icon.icns b/crates/tangle/icons/icon.icns Binary files differ.