From 0db35655ccfe0578576a65edc5d34fa674b296c1 Mon Sep 17 00:00:00 2001
From: Silberengel
Date: Mon, 6 Apr 2026 16:44:13 +0200
Subject: [PATCH] bug-fixes
---
.../secondary/ProfileEditorPage/index.tsx | 222 +++++++++++++++++-
src/providers/NostrProvider/index.tsx | 29 ++-
2 files changed, 239 insertions(+), 12 deletions(-)
diff --git a/src/pages/secondary/ProfileEditorPage/index.tsx b/src/pages/secondary/ProfileEditorPage/index.tsx
index 19f3def9..0f70f90f 100644
--- a/src/pages/secondary/ProfileEditorPage/index.tsx
+++ b/src/pages/secondary/ProfileEditorPage/index.tsx
@@ -26,7 +26,7 @@ import { syncUserDeletionTombstones } from '@/lib/sync-user-deletions'
import { useSecondaryPage } from '@/PageManager'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
-import { ChevronDown, Pencil, Plus, RefreshCw, Trash2, Upload } from 'lucide-react'
+import { ChevronDown, Fingerprint, Pencil, Plus, RefreshCw, Trash2, Upload } from 'lucide-react'
import type { Event } from 'nostr-tools'
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -70,6 +70,9 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
const [refreshingCache, setRefreshingCache] = useState(false)
/** Editable tag list for kind 0 (e.g. lud16, nip05, website). Each row is [name, value]. */
const [profileTags, setProfileTags] = useState([])
+ /** Dialog to set picture/banner URL from JSON fields (alternative to top uploaders). */
+ const [imageUrlField, setImageUrlField] = useState<'picture' | 'banner' | null>(null)
+ const [imageUrlDraft, setImageUrlDraft] = useState('')
const defaultImage = useMemo(
() => (account ? generateImageByPubkey(account.pubkey) : undefined),
[account]
@@ -221,7 +224,10 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
const tagsToSave = profileTags
.filter((tag) => Array.isArray(tag) && tag.length >= 2 && tag[0].trim() && tag[1].trim())
+ .filter((tag) => !isPictureOrBannerTagName(tag[0]))
.map((tag) => [tag[0].trim(), tag[1].trim(), ...(tag.slice(2) || [])])
+ if (avatar.trim()) tagsToSave.push(['picture', avatar.trim()])
+ if (banner.trim()) tagsToSave.push(['banner', banner.trim()])
setSaving(true)
setHasChanged(false)
const profileDraftEvent = createProfileDraftEvent(
@@ -265,7 +271,51 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
}
}, [account?.pubkey, relayList, requestAccountNetworkHydrate, updateProfileEvent, t])
- if (!account || !profile) return null
+ if (!account) return null
+
+ // Profile still loading: show the header with the Refresh Cache button so the user isn't stuck.
+ if (!profile) {
+ const loadingControls = (
+
+
+
+ )
+ return (
+
+
+
+
+ {t('profileEditorProfileNotLoaded', {
+ defaultValue: 'Profile not loaded. Try refreshing the cache.'
+ })}
+
+
+
+ )
+ }
+
+ const openImageUrlEditor = (field: 'picture' | 'banner') => {
+ setImageUrlField(field)
+ setImageUrlDraft(field === 'picture' ? avatar : banner)
+ }
+
+ const applyImageUrlDraft = () => {
+ if (!imageUrlField) return
+ const v = imageUrlDraft.trim()
+ if (imageUrlField === 'picture') setAvatar(v)
+ else setBanner(v)
+ setHasChanged(true)
+ setImageUrlField(null)
+ }
const saveFullProfile = async () => {
let parsed: { kind?: number; content?: string; tags?: string[][] }
@@ -428,7 +478,38 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
{t('Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.')}
- {profileTags.map((tag, idx) => (
+
openImageUrlEditor('picture')}
+ onInsertThumb={() => {
+ const next = insertNostrBuildThumbUrl(avatar)
+ if (next) {
+ setAvatar(next)
+ setHasChanged(true)
+ }
+ }}
+ showThumbButton={canInsertNostrBuildThumb(avatar)}
+ t={t}
+ />
+ openImageUrlEditor('banner')}
+ onInsertThumb={() => {
+ const next = insertNostrBuildThumbUrl(banner)
+ if (next) {
+ setBanner(next)
+ setHasChanged(true)
+ }
+ }}
+ showThumbButton={false}
+ t={t}
+ />
+ {profileTags
+ .map((tag, idx) => ({ tag, idx }))
+ .filter(({ tag }) => !isPictureOrBannerTagName(tag[0]))
+ .map(({ tag, idx }) => (
{
+ {/* Set picture/banner URL (kind 0 JSON content) */}
+
+
{/* Edit payment info dialog */}