web


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

commit afe284c9c7bf816523c2ea8b11ab1542018f59f3
parent 4a97e0b6ba0fb52eb2f17b333c7638a5aae48743
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu, 24 Oct 2024 19:40:06 +0000

Edit `/models/nostr-profile/` routes, add profiles list page trellis and handler utils, edit profile view page layout and display logic, edit profile field edit page submit handler. Edit (app) home page and layout. Add keystore reset to `/cfg/init` submit handler.

Diffstat:
Msrc/routes/(app)/+layout.svelte | 7++-----
Msrc/routes/(app)/+page.svelte | 35+++++++++++++++++++++++------------
Msrc/routes/(app)/models/nostr-profile/+page.svelte | 311+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/routes/(app)/models/nostr-profile/edit/field/+page.svelte | 28+++++++++++++---------------
Msrc/routes/(app)/models/nostr-profile/view/+page.svelte | 144++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/routes/(cfg)/cfg/init/+page.svelte | 2+-
6 files changed, 346 insertions(+), 181 deletions(-)

diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -9,11 +9,8 @@ app_geoc.set(!!geoc_connected); /* - const nostr_publickey = await keystore.get(ks.keys.nostr_publickey); - if (`result` in nostr_publickey) { - await sleep(4000); - await notification.init(); - } + await sleep(4000); + await notification.init(); */ } catch (e) { console.log(`e (app) onMount`, e); diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { notification } from "$lib/client"; + import { db, dialog, notification } from "$lib/client"; import { app_cfg_type, app_layout, @@ -68,6 +68,15 @@ onMount(async () => { try { nav_prev.set([]); + + const nostr_profile = await db.nostr_profile_get({ + public_key: $app_nostr_key, + }); + if (`err` in nostr_profile) { + await dialog.alert(`@todo Nostr profile configuration failure`); + return; + } + if (!nostr_profile.results[0].name) tmp_show_no_profile = true; } catch (e) { } finally { } @@ -92,27 +101,29 @@ <div class={`flex flex-col justify-center items-center`}> {#if tmp_show_no_profile} <button - class={`relative flex flex-col h-24 w-${$app_layout} p-4 justify-center items-center bg-layer-2-surface/60 rounded-touch touch-layer-1 touch-layer-1-raise-less el-re`} + class={`relative flex flex-row h-24 w-${$app_layout} p-4 gap-4 justify-center items-center bg-layer-2-surface/60 rounded-touch touch-layer-1 touch-layer-1-raise-less el-re`} on:click={async () => { await route(`/models/nostr-profile`); }} > - <div class={`flex flex-row gap-2 justify-start items-center`}> + <div + class={`absolute left-6 flex flex-row h-full justify-end items-center`} + > <Glyph basis={{ classes: `text-layer-2-glyph`, key: `user-circle-plus`, - dim: `lg`, + dim: `xl`, weight: `bold`, }} /> + </div> + <div class={`flex flex-col justify-center items-center`}> <p class={`font-circ font-[500] text-lg text-layer-2-glyph/90`} > {`No farm profile added.`} </p> - </div> - <div class={`flex flex-row justify-start items-center`}> <p class={`font-circ font-[500] text-sm text-layer-2-glyph/90`} > @@ -132,8 +143,8 @@ </div> <div class={`flex flex-col w-full gap-2 justify-start items-center`}> <div class={`flex flex-row w-full px-8 justify-start items-center`}> - <p class={`font-circ font-[500] text-layer-0-glyph`}> - {`${$t(`common.options_list`)}`} + <p class={`font-sans font-[500] text-layer-0-glyph capitalize`}> + {`${$t(`common.options_list`)}:`} </p> </div> <div class={`flex flex-col w-full gap-4 justify-start items-center`}> @@ -185,26 +196,26 @@ { icon: `house-line`, label: `Home`, - callback: async (tab_i) => { + callback: async () => { await route(`/`); }, }, { icon: `arrows-down-up`, label: `Transactions`, - callback: async (tab_i) => {}, + callback: async () => {}, }, { icon: `cardholder`, label: `Wallet`, - callback: async (tab_i) => { + callback: async () => { await route(`/models/nostr-profile`); }, }, { icon: `squares-four`, label: `Menu`, - callback: async (tab_i) => { + callback: async () => { await route(`/settings`); }, }, diff --git a/src/routes/(app)/models/nostr-profile/+page.svelte b/src/routes/(app)/models/nostr-profile/+page.svelte @@ -1,21 +1,56 @@ <script lang="ts"> - import { db, keystore } from "$lib/client"; - import { - nostr_profile_form_vals, - parse_nostr_profile_form_keys, - type NostrProfile, - } from "@radroots/models"; + import { db, keystore, nostr } from "$lib/client"; + import { type NostrProfile } from "@radroots/models"; import { + app_nostr_key, app_notify, - as_glyph_key, + Glyph, + type ISelectOption, LayoutTrellis, LayoutView, Nav, + nav_prev, + route, + SelectElement, t, - Trellis, + TrellisTitle, } from "@radroots/svelte-lib"; import { onMount } from "svelte"; + type OptionsListKey = + | `view-key` + | `set-key-active` + | `edit-profile-name` + | `add-profile-name` + | `delete-key`; + type OptionsList = ISelectOption<OptionsListKey>; + const page_param: { + options_list: OptionsList[]; + } = { + options_list: [ + { + value: `view-key`, + label: `${$t(`icu.view_*`, { value: `${$t(`common.details`)}`.toLowerCase() })}`, + }, + { + value: `edit-profile-name`, + label: `${$t(`icu.edit_*`, { value: `${$t(`common.profile_name`)}`.toLowerCase() })}`, + }, + { + value: `add-profile-name`, + label: `${$t(`icu.add_*`, { value: `${$t(`common.profile_name`)}`.toLowerCase() })}`, + }, + { + value: `set-key-active`, + label: `${$t(`icu.set_as_*`, { value: `${$t(`common.active`)}`.toLowerCase() })}`, + }, + { + value: `delete-key`, + label: `${$t(`icu.delete_*`, { value: `${$t(`common.key`)}`.toLowerCase() })}`, + }, + ], + }; + type LoadData = { nostr_profiles: NostrProfile[]; }; @@ -38,6 +73,10 @@ const nostr_profiles = await db.nostr_profile_get({ list: [`all`], }); + console.log( + JSON.stringify(nostr_profiles, null, 4), + `nostr_profiles`, + ); if (`err` in nostr_profiles) { app_notify.set(`${$t(`error.client.page.load`)}`); return; @@ -63,88 +102,208 @@ const handle_add_nostr_profile = async (): Promise<void> => { try { - const ks_keys = await keystore.keys(); + const ks_keys = await keystore.entries(); console.log(JSON.stringify(ks_keys, null, 4), `ks_keys`); } catch (e) { console.log(`(error) handle_add_nostr_profile `, e); } }; + + const handle_key_options_press = async (opts: { + option: string; + public_key: string; + }): Promise<void> => { + switch (opts.option) { + case `view-key`: + case `add-profile-name`: + case `edit-profile-name`: { + $nav_prev.push({ + route: `/models/nostr-profile/view`, + label: `Keys`, + }); + } + + case `view-key`: + { + await route(`/models/nostr-profile/view`, [ + [`nostr_pk`, opts.public_key], + ]); + } + break; + case `add-profile-name`: + case `edit-profile-name`: + { + await route(`/models/nostr-profile/edit/field`, [ + [`nostr_pk`, opts.public_key], + [`rkey`, `name`], + ]); + } + break; + } + }; </script> <LayoutView> - <LayoutTrellis> - {#if ld} - {#each ld.nostr_profiles as li} - <Trellis + {#if ld} + <LayoutTrellis> + <div + class={`flex flex-col w-full gap-[2px] justify-start items-center`} + > + <TrellisTitle + layer={0} basis={{ - args: { - layer: 1, - title: { - value: `${$t(`icu.your_*`, { value: `${$t(`common.profiles`)}` })}`, - }, - list: [ - ...Object.keys(nostr_profile_form_vals).map( - (k) => ({ - hide_active: true, - touch: { - label: { - left: [ - { - classes: `capitalize`, - value: `${$t(`model.nostr_profile.${k}`, { default: k.replaceAll(`_`, ` `) })}`, - }, - ], - right: [ - { - classes: `font-[300] text-layer-1-glyph-shade`, - value: - Object.assign(li)[ - parse_nostr_profile_form_keys( - k, - ) - ] || "(none)", - }, - ], + value: `${$t(`icu.*_list`, { value: `${$t(`common.profile`)}`.toLowerCase() })}`, + }} + /> + {#if ld.nostr_profiles.length} + {#each ld.nostr_profiles as li (li.public_key)} + <div + class={`relative flex flex-col h-24 pt-5 px-3 bg-layer-1-surface rounded-touch overflow-hidden active:ring-4 active:ring-layer-2-surface/80 transition-all tap-rise-1 active:opacity-60`} + > + <button + class={`flex flex-col h-full w-full pt-[2px] pl-1 gap-1 items-start`} + on:click|preventDefault={async () => { + $nav_prev.push({ + route: `/models/nostr-profile`, + label: `${$t(`common.profiles`)}`, + }); + await route(`/models/nostr-profile/view`, [ + [`nostr_pk`, li.public_key], + ]); + }} + > + <div + class={`flex flex-row w-full pl-1 gap-4 justify-start items-center`} + > + <p + class={`font-mono text-[1.1rem] text-layer-1-glyph-shade text-ellipsis overflow-hidden`} + > + {li.name + ? li.name + : `(${`${$t(`icu.no_*`, { value: `${$t(`common.profile`)}` })}`})`} + </p> + {#if li.public_key === $app_nostr_key} + <div class={`flex flex-row`}> + <div + class={`flex flex-row h-4 justify-center items-center px-[6px] bg-success/70 rounded-md -translate-y-[1px]`} + > + <p + class={`font-mono font-[900] text-[0.7rem] text-white text-ellipsis overflow-hidden`} + > + {`${$t(`common.active`)}`} + </p> + </div> + </div> + {/if} + </div> + <div + class={`grid grid-cols-12 flex flex-row h-6 w-full pt-2 gap-2 items-center`} + > + <div + class={`col-span-2 flex flex-row h-full items-center `} + > + <div + class={`flex flex-row h-[1rem] px-[9px] justify-start items-center bg-zinc-800/90 rounded-[5px] translate-y-[1px]`} + > + <p + class={`font-mono font-[600] text-[0.9rem] text-layer-2-glyph lowercase line-clamp-1`} + > + {`${$t(`common.key`)}`} + </p> + </div> + </div> + <div + class={`col-span-10 flex flex-row h-full pr-2 justify-end items-center overflow-x-hidden`} + > + <p + class={`font-mono text-[0.9rem] text-layer-1-glyph line-clamp-1`} + > + {`${`${nostr.lib.npub(li.public_key) || ""}`.slice( + 0, + 24, + )}...`} + </p> + </div> + </div> + </button> + <div + class={`z-10 absolute top-2 right-3 flex flex-row h-full justify-end pr-1`} + > + <SelectElement + basis={{ + args: { + layer: 0, + mask: true, + callback: async ({ value }) => { + await handle_key_options_press({ + option: value, + public_key: li.public_key, + }); }, - end: { - icon: { - key: as_glyph_key( - `caret-right`, - ), + options: [ + { + entries: + page_param.options_list.filter( + (i) => + !( + !li.name && + i.value === + `edit-profile-name` + ) && + !( + li.name && + i.value === + `add-profile-name` + ) && + !( + li.public_key === + $app_nostr_key && + i.value === + `set-key-active` + ), + ), }, - }, - callback: async () => {}, + ], }, - }), - ), - ], - }, - }} - /> - {/each} - {:else} - <div - class={`flex flex-col w-full justify-center items-center px-4 gap-3`} + }} + > + <svelte:fragment slot="element"> + <Glyph + basis={{ + key: `dots-three`, + dim: `md`, + classes: `text-layer-1-glyph`, + weight: `bold`, + }} + /> + </svelte:fragment> + </SelectElement> + </div> + </div> + {/each} + {/if} + </div> + </LayoutTrellis> + {:else} + <div + class={`flex flex-col w-full justify-center items-center px-4 gap-3`} + > + <p class={`font-sans font-[400] text-layer-2-glyph`}> + {`No items to display.`} + </p> + + <button + class={`flex flex-row justify-center items-center`} + on:click={async () => { + await handle_add_location_gcs(); + }} > - <p class={`font-sans font-[400] text-layer-2-glyph`}> - {`No items to display.`} + <p class={`font-sans font-[400] text-layer-2-glyph-hl text-sm`}> + {`Click to add a new location`} </p> - - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - await handle_add_location_gcs(); - }} - > - <p - class={`font-sans font-[400] text-layer-2-glyph-hl text-sm`} - > - {`Click to add a new location`} - </p> - </button> - </div> - {/if} - </LayoutTrellis> + </button> + </div> + {/if} </LayoutView> <Nav basis={{ diff --git a/src/routes/(app)/models/nostr-profile/edit/field/+page.svelte b/src/routes/(app)/models/nostr-profile/edit/field/+page.svelte @@ -18,6 +18,7 @@ qp_nostr_pk, qp_rkey, route, + route_prev, sleep, t, Trellis, @@ -58,10 +59,14 @@ }); let val_field_valid = false; + $: translated_field_key = ld?.field_key ? `${$t(`model.nostr_profile.${ld?.field_key}`, { default: ld?.field_key?.replaceAll(`_`, ` `) })}`.toLowerCase() : ``; + $: if (el_input_loaded && el_input) { + el_input.focus(); + } const load_page = async (): Promise<LoadData | undefined> => { try { const nostr_profiles = await db.nostr_profile_get({ @@ -123,28 +128,21 @@ fields, }); if (`err` in update_res) { - await dialog.alert(`${$t(`common.error.client.unhandled`)}`); - return; //@todo + await dialog.alert(`${$t(`error.client.unhandled`)}`); + return; } - alert(`@todo sync to nostr`); // @todo sync to nostr - //if ($app_submit_route) { - // await route($app_submit_route.route, $app_submit_route.params); - //} else { - // await route(`/nostr/keys`); - //} + if (ld) + await route_prev(`/models/nostr-profile/view`, [ + [`id`, ld.nostr_profile.id], + ]); + else await route_prev(`/models/nostr-profile`); return; } catch (e) { console.log(`(error) submit `, e); } }; - - $: { - if (el_input_loaded && el_input) { - el_input.focus(); - } - } </script> <LayoutView> @@ -197,7 +195,7 @@ }, on_mount: async (el) => { el_input = el; - await sleep(600); + await sleep(600); //@todo el_input_loaded = true; }, }, diff --git a/src/routes/(app)/models/nostr-profile/view/+page.svelte b/src/routes/(app)/models/nostr-profile/view/+page.svelte @@ -49,6 +49,10 @@ const nostr_profiles = await db.nostr_profile_get({ public_key: $qp_nostr_pk, }); + console.log( + JSON.stringify(nostr_profiles, null, 4), + `nostr_profiles`, + ); if (`err` in nostr_profiles) { app_notify.set(`${$t(`error.client.page.load`)}`); return; @@ -173,6 +177,72 @@ args: { layer: 1, title: { + value: `${$t(`common.profile_name`)}`, + }, + list: [ + { + hide_active: vl_secret_key_unlock, + touch: { + label: { + left: [ + { + classes: ld.nostr_profile.name + ? `` + : `text-layer-1-glyph-shade`, + value: + ld.nostr_profile.name || + `${$t(`icu.no_*_published`, { value: `${$t(`common.profile`)}`.toLowerCase() })}`, + }, + ], + }, + end: { + icon: { + key: as_glyph_key(`caret-right`), + }, + }, + callback: async () => { + if (!ld) return; + app_submit_route.set({ + route: `/models/nostr-profile/view`, + params: [ + [ + `nostr_pk`, + ld.nostr_profile.public_key, + ], + ], + }); + $nav_prev.push({ + route: `/models/nostr-profile/view`, + label: `Key`, + params: [ + [ + `nostr_pk`, + ld.nostr_profile.public_key, + ], + ], + }); + await route( + `/models/nostr-profile/edit/field`, + [ + [ + `nostr_pk`, + ld.nostr_profile.public_key, + ], + [`rkey`, `name`], + ], + ); + }, + }, + }, + ], + }, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { value: `${$t(`common.public_key`)}`, link: { label: { @@ -393,10 +463,7 @@ .name ? `` : `text-layer-1-glyph-shade`, - value: - ld.nostr_profile - .name || - `${$t(`icu.no_*_published`, { value: `${$t(`common.relays`)}`.toLowerCase() })}`, + value: `${$t(`icu.no_*_published`, { value: `${$t(`common.relays`)}`.toLowerCase() })}`, }, ], }, @@ -406,73 +473,6 @@ }, }} /> - - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$t(`common.profile_name`)}`, - }, - list: [ - { - hide_active: vl_secret_key_unlock, - touch: { - label: { - left: [ - { - classes: ld.nostr_profile.name - ? `` - : `text-layer-1-glyph-shade`, - value: - ld.nostr_profile.name || - `${$t(`icu.no_*_published`, { value: `${$t(`common.profile`)}`.toLowerCase() })}`, - }, - ], - }, - end: { - icon: { - key: as_glyph_key(`caret-right`), - }, - }, - callback: async () => { - if (!ld) return; - app_submit_route.set({ - route: `/models/nostr-profile/view`, - params: [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - ], - }); - $nav_prev.push({ - route: `/models/nostr-profile/view`, - label: `Key`, - params: [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - ], - }); - await route( - `/models/nostr-profile/edit/field`, - [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - [`rkey`, `name`], - ], - ); - }, - }, - }, - ], - }, - }} - /> {/if} </LayoutTrellis> </LayoutView> @@ -485,7 +485,7 @@ title: { label: { classes: `capitalize`, - value: `${$t(`common.key`)}`, + value: `${$t(`common.profile`)}`, }, }, option: { diff --git a/src/routes/(cfg)/cfg/init/+page.svelte b/src/routes/(cfg)/cfg/init/+page.svelte @@ -127,7 +127,6 @@ const reset_page = async (alert_message?: string): Promise<void> => { try { - //@todo call reset_ks ? app_loading.set(true); handle_view(`cfg_init`); if (alert_message) await dialog.alert(alert_message); @@ -628,6 +627,7 @@ ks.keys.nostr_secretkey(nostr_publickey), ks_nostr_secretkey.result, ); + await reset_ks(); await restart({ route: `/`, notify_message: `${$t(`app.page.cfg.init.notification.welcome`)}`,