commit 42f720316db17adbd4a973f063fb7f3b423ffe87
parent 59117e9a992dc37f2d066d910c89270787697003
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 8 Mar 2025 00:11:15 +0000
Edit `model` crate adding update method result pass return types. Add app client nostr sync util. Edit profile views, layouts.
Diffstat:
12 files changed, 347 insertions(+), 132 deletions(-)
diff --git a/app/src/lib/util/nostr/sync.ts b/app/src/lib/util/nostr/sync.ts
@@ -0,0 +1,21 @@
+import { get_store, handle_err, ndk_user } from "@radroots/lib-app";
+import { throw_err } from "@radroots/util";
+import { db, nostrsync } from "..";
+
+export const nostr_sync_metadata = async (): Promise<void> => {
+ try {
+ const $ndk_user = get_store(ndk_user);
+ const tb_nostr_profile = await db.nostr_profile_read({
+ public_key: $ndk_user?.pubkey,
+ });
+ if (`err` in tb_nostr_profile) throw_err(tb_nostr_profile.err);
+ const ev_metadata = await nostrsync.metadata({
+ metadata: tb_nostr_profile.result,
+ });
+ if (`err` in ev_metadata) throw_err(ev_metadata.err);
+ await ev_metadata.publish();
+ } catch (e) {
+ await handle_err(e, `nostr_sync_metadata`);
+ //location.reload(); @todo
+ }
+};
+\ No newline at end of file
diff --git a/app/src/routes/(app)/+layout.svelte b/app/src/routes/(app)/+layout.svelte
@@ -1,6 +1,7 @@
<script lang="ts">
- import { datastore, db, http, keys } from "$lib/util";
+ import { datastore, db, gui, http, keys } from "$lib/util";
import {
+ app_notify,
app_splash,
handle_err,
idb_init,
@@ -11,6 +12,7 @@
import { ndk_init } from "@radroots/nostr-util";
import { throw_err } from "@radroots/util";
+ import { nostr_sync_metadata } from "$lib/util/nostr/sync";
import { onMount } from "svelte";
import type { LayoutProps } from "./$types";
@@ -41,15 +43,13 @@
const nostr_init = async (): Promise<void> => {
try {
- if (!data.public_key) return void throw_err(`*-key_nostr`);
+ if (!data.public_key) throw_err(`*-key_nostr`);
const keys_nostr_read = await keys.nostr_read(data.public_key);
- if (`err` in keys_nostr_read)
- return void throw_err(keys_nostr_read.err);
+ if (`err` in keys_nostr_read) throw_err(keys_nostr_read.err);
const tb_nostr_relays = await db.nostr_relay_read_list({
table: [`on_profile`, { public_key: data.public_key }],
});
- if (`err` in tb_nostr_relays)
- return void throw_err(tb_nostr_relays.err);
+ if (`err` in tb_nostr_relays) throw_err(tb_nostr_relays.err);
for (const { url } of tb_nostr_relays.results)
$ndk.addExplicitRelay(url);
await $ndk.connect();
@@ -58,13 +58,21 @@
secret_key: keys_nostr_read.secret_key,
});
nostr_ndk_configured.set(!!ndk_user_init);
- if (!ndk_user_init) return; //@todo
+ if (!ndk_user_init) throw_err(`error.nostr.ndk_user_undefined`);
$ndk_user = ndk_user_init;
$ndk_user.ndk = $ndk;
+ await nostr_sync_metadata();
} catch (e) {
await handle_err(e, `nostr_init`);
}
};
+
+ app_notify.subscribe(async (_app_notify) => {
+ console.log(`_app_notify `, _app_notify);
+ if (!_app_notify) return;
+ await gui.notify_send($app_notify);
+ app_notify.set(``);
+ });
</script>
{@render children()}
diff --git a/app/src/routes/(app)/profile/+page.svelte b/app/src/routes/(app)/profile/+page.svelte
@@ -1,94 +1,178 @@
<script lang="ts">
import { ls } from "$lib/locale/i18n";
- import { db, fs, gui, nostrsync, route } from "$lib/util";
- import { handle_err, ndk_user, Profile } from "@radroots/lib-app";
- import { throw_err } from "@radroots/util";
+ import { db, fs, gui, keys, radroots, route } from "$lib/util";
+ import { nostr_sync_metadata } from "$lib/util/nostr/sync";
+ import {
+ handle_err,
+ ndk_user,
+ Profile,
+ type IViewProfileData,
+ } from "@radroots/lib-app";
+ import { parse_file_path, throw_err, type FilePath } from "@radroots/util";
+ import { onMount } from "svelte";
+ let data: IViewProfileData | undefined = $state(undefined);
let loading_photo_upload = $state(false);
- let photo_path_opt = $state(``);
-</script>
+ let loading_photo_upload_open = $state(false);
+ let photo_path = $state(``);
-<Profile
- bind:photo_path_opt
- {ls}
- basis={{
- loading_photo_upload,
- lc_on_destroy: async () => {
- try {
- const tb_nostrprofile = await db.nostr_profile_read({
- public_key: $ndk_user.pubkey,
- });
- if (`err` in tb_nostrprofile) throw_err(tb_nostrprofile.err); //@todo
- const ev_metadata = await nostrsync.metadata({
- metadata: tb_nostrprofile.result,
- });
- if (`err` in ev_metadata)
- throw_err(`error.nostr.sync.missing_metadata_event`);
- await ev_metadata.publish();
- } catch (e) {
- await handle_err(e, `lc_on_destroy`);
+ onMount(async () => {
+ try {
+ const tb_nostr_profile = await db.nostr_profile_read({
+ public_key: $ndk_user?.pubkey,
+ });
+ console.log(
+ JSON.stringify(tb_nostr_profile, null, 4),
+ `tb_nostr_profile`,
+ );
+ if (`result` in tb_nostr_profile) {
+ data = { ...tb_nostr_profile.result };
+ nostr_sync_metadata(); // no await
+ return;
}
- },
- lc_handle_back: async () => {
- try {
- if (photo_path_opt) {
- const confirm = await gui.confirm({
- message: `${$ls(`notification.profile.update_name_confirmation_on_route_previous`)}`,
- ok: `${$ls(`common.upload_photo`)}`,
- cancel: `${$ls(`common.keep_previous`)}`,
+ return void (await route(`/`));
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+</script>
+
+{#if data}
+ <Profile
+ bind:photo_path
+ {ls}
+ basis={{
+ data,
+ loading_photo_upload,
+ loading_photo_upload_open,
+ lc_on_destroy: async ({ public_key }) => {
+ try {
+ const tb_nostrprofile = await db.nostr_profile_read({
+ public_key,
});
- if (!confirm) return void (await route(`/`));
- // @todo upload photo
+ if (`err` in tb_nostrprofile)
+ throw_err(tb_nostrprofile.err); //@todo
+ await nostr_sync_metadata();
+ } catch (e) {
+ await handle_err(e, `lc_on_destroy`);
}
- await route(`/`);
- } catch (e) {
- await handle_err(e, `lc_handle_back`);
- }
- },
- lc_handle_edit_profile_field: async ({ field }) => {
- try {
- //@todo
- if (field === `name`) {
- const confirm = await gui.confirm({
- message: `${$ls(`notification.profile.update_name_confirmation`)}. ${$ls(`common.do_you_want_to_continue_q`)}`,
- ok: `${$ls(`common.continue`)}`,
- cancel: `${$ls(`common.cancel`)}`,
+ },
+ lc_handle_back: async ({ is_photo_existing }) => {
+ try {
+ const public_key = $ndk_user?.pubkey;
+ if (!photo_path || !public_key)
+ return void (await route(`/`));
+ const keys_nostr_read = await keys.nostr_read(public_key);
+ if (`err` in keys_nostr_read)
+ throw_err(keys_nostr_read.err);
+ if (photo_path) {
+ const confirm = await gui.confirm({
+ message: is_photo_existing
+ ? `${$ls(`notification.profile.handle_back_with_selected_photo`)}`
+ : `${$ls(`notification.profile.handle_back_with_selected_photo_no_existing`)}`,
+ ok: `${$ls(`common.upload_photo`)}`,
+ cancel: is_photo_existing
+ ? `${$ls(`common.keep_previous`)}`
+ : `${$ls(`common.continue`)}`,
+ });
+ if (!confirm) return void (await route(`/`));
+ }
+ loading_photo_upload = true;
+ let upload_file_path: FilePath | undefined = undefined;
+ parse_file_path(photo_path);
+ const tb_media_upload_existing = await db.media_image_read({
+ file_path: photo_path,
});
- if (!confirm) return;
+ if (`result` in tb_media_upload_existing)
+ upload_file_path = parse_file_path(
+ tb_media_upload_existing.result.file_path,
+ );
+ else upload_file_path = parse_file_path(photo_path);
+ if (!upload_file_path)
+ throw_err(`error.util.parse_file_path`);
+ const file_data = await fs.read_bin(
+ upload_file_path.file_path,
+ );
+ const res_fetch_media_image_upload =
+ await radroots.fetch_media_image_upload({
+ file_path: upload_file_path,
+ file_data,
+ secret_key: keys_nostr_read.secret_key,
+ });
+ if (`err` in res_fetch_media_image_upload)
+ throw_err(res_fetch_media_image_upload.err);
+ const {
+ res_base: upload_res_base,
+ res_path: upload_res_path,
+ } = res_fetch_media_image_upload;
+ const tb_media_image_create = await db.media_image_create({
+ file_path: upload_file_path.file_path,
+ res_base: upload_res_base,
+ res_path: upload_res_path,
+ mime_type: upload_file_path.mime_type,
+ });
+ if (`err` in tb_media_image_create)
+ throw_err(tb_media_image_create.err);
+ const tb_nostr_profile_update =
+ await db.nostr_profile_update({
+ filter: { public_key },
+ fields: {
+ picture: `${upload_res_base}/${upload_res_path}.${upload_file_path.mime_type}`,
+ },
+ });
+ if (`err` in tb_nostr_profile_update)
+ throw_err(tb_nostr_profile_update.err);
+ await route(`/`);
+ } catch (e) {
+ await handle_err(e, `lc_handle_back`);
+ } finally {
+ loading_photo_upload = false;
}
- await route(`/profile/edit`, [
- [`key_nostr`, $ndk_user.pubkey],
- [`field`, field],
- ]);
- } catch (e) {
- await handle_err(e, `lc_handle_edit_profile_field`);
- }
- },
- lc_handle_photo_add: async () => {
- try {
- loading_photo_upload = true;
- const photo_paths_open = await gui.open_photos();
- if (!photo_paths_open) return;
- const photo_path_add = photo_paths_open.results[0];
- if (photo_path_add) return photo_path_add;
- } catch (e) {
- await handle_err(e, `lc_handle_photo_add`);
- } finally {
- loading_photo_upload = false;
- }
- },
- lc_handle_photo_options: async () => {
- try {
- } catch (e) {
- await handle_err(e, `lc_handle_photo_options`);
- }
- },
- lc_fs_read_bin: async (file_path) => {
- try {
- return await fs.read_bin(file_path);
- } catch (e) {
- await handle_err(e, `lc_fs_read_bin`);
- }
- },
- }}
-/>
+ },
+ lc_handle_edit_profile_field: async ({ field }) => {
+ try {
+ if (field === `name`) {
+ const confirm = await gui.confirm({
+ message: `${$ls(`notification.profile.update_name_confirmation`)}. ${$ls(`common.do_you_want_to_continue_q`)}`,
+ ok: `${$ls(`common.continue`)}`,
+ cancel: `${$ls(`common.cancel`)}`,
+ });
+ if (!confirm) return;
+ }
+ await route(`/profile/edit`, [
+ [`key_nostr`, $ndk_user?.pubkey],
+ [`field`, field],
+ ]);
+ } catch (e) {
+ await handle_err(e, `lc_handle_edit_profile_field`);
+ }
+ },
+ lc_handle_photo_add: async () => {
+ try {
+ loading_photo_upload_open = true;
+ const photo_paths_open = await gui.open_photos();
+ if (!photo_paths_open) return;
+ const photo_path_add = photo_paths_open.results[0];
+ if (photo_path_add) return photo_path_add;
+ } catch (e) {
+ await handle_err(e, `lc_handle_photo_add`);
+ } finally {
+ loading_photo_upload_open = false;
+ }
+ },
+ lc_handle_photo_options: async () => {
+ try {
+ } catch (e) {
+ await handle_err(e, `lc_handle_photo_options`);
+ }
+ },
+ lc_fs_read_bin: async (file_path) => {
+ try {
+ return await fs.read_bin(file_path);
+ } catch (e) {
+ await handle_err(e, `lc_fs_read_bin`);
+ }
+ },
+ }}
+ />
+{/if}
diff --git a/app/src/routes/(app)/profile/edit/+page.svelte b/app/src/routes/(app)/profile/edit/+page.svelte
@@ -1,14 +1,103 @@
<script lang="ts">
- import { goto } from "$app/navigation";
+ import { ls } from "$lib/locale/i18n";
+ import { db, gui, route } from "$lib/util";
+ import { nostr_sync_metadata } from "$lib/util/nostr/sync";
+ import {
+ handle_err,
+ type IViewProfileEditData,
+ parse_view_profile_field_key,
+ ProfileEdit,
+ qp_field,
+ qp_keynostr,
+ } from "@radroots/lib-app";
+ import { throw_err } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ type LoadData = IViewProfileEditData | undefined;
+ let data: LoadData = $state(undefined);
+
+ let val_field_init = $state(``);
+ let val_field = $state(``);
+
+ onMount(async () => {
+ try {
+ data = await load();
+ if (!data) return void (await route(`/`)); //@todo
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ const load = async (): Promise<LoadData> => {
+ try {
+ const field = parse_view_profile_field_key($qp_field);
+ if (!field || !$qp_keynostr) return void (await route(`/`));
+ const tb_nostr_profile = await db.nostr_profile_read({
+ public_key: $qp_keynostr,
+ });
+ console.log(
+ JSON.stringify(tb_nostr_profile, null, 4),
+ `tb_nostr_profile`,
+ );
+ if (`err` in tb_nostr_profile) return void (await route(`/`));
+ if (field in tb_nostr_profile.result)
+ val_field = tb_nostr_profile.result[field] || ``;
+ val_field_init = val_field;
+ return {
+ public_key: tb_nostr_profile.result.public_key,
+ field,
+ } satisfies IViewProfileEditData;
+ } catch (e) {
+ await handle_err(e, `load`);
+ }
+ };
</script>
-<div class={`flex flex-col w-full pt-32 justify-center items-center`}>
- <button
- class={`flex flex-row justify-center items-center`}
- onclick={async () => {
- await goto(`/`);
+{#if data}
+ <ProfileEdit
+ bind:val_field
+ {ls}
+ basis={{
+ data,
+ lc_handle_back: async ({ field, public_key }) => {
+ try {
+ if (val_field_init === val_field)
+ return void (await route(`/profile`));
+ const confirm = await gui.confirm({
+ message: `${$ls(`notify.profile.name_update`)}. ${$ls(`common.do_you_want_to_continue_q`)}`,
+ });
+ if (!confirm) return;
+ const nostr_profile_update = await db.nostr_profile_update({
+ filter: {
+ public_key,
+ },
+ fields: {
+ [field]: val_field,
+ },
+ });
+ if (`err` in nostr_profile_update)
+ throw_err(nostr_profile_update.err);
+ const tb_nostr_profile = await db.nostr_profile_read({
+ public_key,
+ });
+ console.log(
+ JSON.stringify(tb_nostr_profile, null, 4),
+ `tb_nostr_profile`,
+ );
+ if (`err` in tb_nostr_profile)
+ throw_err(tb_nostr_profile.err);
+ nostr_sync_metadata(); // no await
+ await route(`/profile`);
+ } catch (e) {
+ await handle_err(e, `lc_handle_back`);
+ }
+ },
+ lc_handle_input: async () => {
+ try {
+ } catch (e) {
+ await handle_err(e, `lc_handle_input`);
+ }
+ },
}}
- >
- profile edit
- </button>
-</div>
+ />
+{/if}
diff --git a/app/src/routes/(cfg)/init/+page.svelte b/app/src/routes/(cfg)/init/+page.svelte
@@ -2,7 +2,15 @@
import { goto } from "$app/navigation";
import { PUBLIC_NOSTR_RELAY_DEFAULTS } from "$env/static/public";
import { ls } from "$lib/locale/i18n";
- import { datastore, db, gui, keys, nostrkey, radroots } from "$lib/util";
+ import {
+ datastore,
+ db,
+ gui,
+ keys,
+ nostrkey,
+ radroots,
+ route,
+ } from "$lib/util";
import { cfg_delay } from "$lib/util/conf";
import {
app_lo,
@@ -24,7 +32,13 @@
LogoCircle,
view_effect,
} from "@radroots/lib-app";
- import { el_id, form_fields, sleep, type ResultPass } from "@radroots/util";
+ import {
+ el_id,
+ form_fields,
+ sleep,
+ str_capitalize_words,
+ type ResultPass,
+ } from "@radroots/util";
import { onMount } from "svelte";
type IdbKey = `nostr:key:add` | `nostr:profile` | `#key_nostrp`;
@@ -346,8 +360,20 @@
kv_keynostrp,
await kv.read(`nostr:profile`),
);
- if (configuration_result && `pass` in configuration_result)
- app_notify.set(`${$ls(`notification.init.on_complete`)}`);
+ if (configuration_result && `pass` in configuration_result) {
+ const confirm = await gui.confirm({
+ message: `${$ls(`notification.init.on_complete`)}`,
+ ok: `${$ls(`common.continue`)}`,
+ cancel: str_capitalize_words(
+ `${$ls(`common.hide_alerts`)}`,
+ ),
+ });
+ if (confirm) {
+ await gui.notify_init();
+ app_notify.set(`${$ls(`notification.init.on_first_load`)}`);
+ }
+ await route(`/`);
+ }
} catch (e) {
await handle_err(e, `submit`);
} finally {
diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte
@@ -1,10 +1,6 @@
<script lang="ts">
- import { gui, route } from "$lib/util";
- import { cfg_delay } from "$lib/util/conf";
import {
app_lo,
- app_loading,
- app_notify,
app_th,
app_thc,
LayoutWindow,
@@ -12,7 +8,7 @@
win_h,
} from "@radroots/lib-app";
import { parse_color_mode, parse_theme_key } from "@radroots/theme";
- import { cfg_app, sleep } from "@radroots/util";
+ import { cfg_app } from "@radroots/util";
import "css-paint-polyfill";
import "../app.css";
import type { LayoutProps } from "./$types";
@@ -31,16 +27,6 @@
if (_win_h > cfg_app.layout.ios1.h) app_lo.set(`ios1`);
else app_lo.set(`ios0`);
});
-
- app_notify.subscribe(async (_app_notify) => {
- if (!_app_notify) return;
- app_loading.set(true);
- await sleep(cfg_delay.notify);
- route(`/`);
- app_loading.set(false);
- await gui.alert(_app_notify);
- app_notify.set(``);
- });
</script>
<LayoutWindow>
diff --git a/crates/model/src/tables/location_gcs.rs b/crates/model/src/tables/location_gcs.rs
@@ -230,7 +230,7 @@ pub struct ILocationGcsQueryUpdate {
}
pub type ILocationGcsUpdate = ILocationGcsQueryUpdate;
-pub type ILocationGcsUpdateResolve = ();
+pub type ILocationGcsUpdateResolve = IResultPass;
pub async fn lib_model_location_gcs_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -244,7 +244,7 @@ pub async fn lib_model_location_gcs_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type ILocationGcsDelete = LocationGcsQueryBindValues;
diff --git a/crates/model/src/tables/log_error.rs b/crates/model/src/tables/log_error.rs
@@ -202,7 +202,7 @@ pub struct ILogErrorQueryUpdate {
}
pub type ILogErrorUpdate = ILogErrorQueryUpdate;
-pub type ILogErrorUpdateResolve = ();
+pub type ILogErrorUpdateResolve = IResultPass;
pub async fn lib_model_log_error_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -216,7 +216,7 @@ pub async fn lib_model_log_error_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type ILogErrorDelete = LogErrorQueryBindValues;
diff --git a/crates/model/src/tables/media_image.rs b/crates/model/src/tables/media_image.rs
@@ -194,7 +194,7 @@ pub struct IMediaImageQueryUpdate {
}
pub type IMediaImageUpdate = IMediaImageQueryUpdate;
-pub type IMediaImageUpdateResolve = ();
+pub type IMediaImageUpdateResolve = IResultPass;
pub async fn lib_model_media_image_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -208,7 +208,7 @@ pub async fn lib_model_media_image_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type IMediaImageDelete = MediaImageQueryBindValues;
diff --git a/crates/model/src/tables/nostr_profile.rs b/crates/model/src/tables/nostr_profile.rs
@@ -210,7 +210,7 @@ pub struct INostrProfileQueryUpdate {
}
pub type INostrProfileUpdate = INostrProfileQueryUpdate;
-pub type INostrProfileUpdateResolve = ();
+pub type INostrProfileUpdateResolve = IResultPass;
pub async fn lib_model_nostr_profile_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -224,7 +224,7 @@ pub async fn lib_model_nostr_profile_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type INostrProfileDelete = NostrProfileQueryBindValues;
diff --git a/crates/model/src/tables/nostr_relay.rs b/crates/model/src/tables/nostr_relay.rs
@@ -210,7 +210,7 @@ pub struct INostrRelayQueryUpdate {
}
pub type INostrRelayUpdate = INostrRelayQueryUpdate;
-pub type INostrRelayUpdateResolve = ();
+pub type INostrRelayUpdateResolve = IResultPass;
pub async fn lib_model_nostr_relay_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -224,7 +224,7 @@ pub async fn lib_model_nostr_relay_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type INostrRelayDelete = NostrRelayQueryBindValues;
diff --git a/crates/model/src/tables/trade_product.rs b/crates/model/src/tables/trade_product.rs
@@ -236,7 +236,7 @@ pub struct ITradeProductQueryUpdate {
}
pub type ITradeProductUpdate = ITradeProductQueryUpdate;
-pub type ITradeProductUpdateResolve = ();
+pub type ITradeProductUpdateResolve = IResultPass;
pub async fn lib_model_trade_product_update(
db: &sqlx::Pool<sqlx::Sqlite>,
@@ -250,7 +250,7 @@ pub async fn lib_model_trade_product_update(
.execute(db)
.await
.map_err(|e| ModelError::InvalidQuery(e.to_string()))?;
- Ok(())
+ Ok(IResultPass { pass: true })
}
pub type ITradeProductDelete = TradeProductQueryBindValues;