From de7204a051040928624f5d185c2a33427f81253b Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 25 Oct 2025 09:57:09 +0200 Subject: [PATCH] bug-fix collapsable side panels and vite URLs --- src/PageManager.tsx | 22 ++++++++++++++++++ src/components/Note/Highlight/index.tsx | 14 +++++++----- src/components/ProfileCard/index.tsx | 19 ++++++++++++++++ src/components/UserAvatar/index.tsx | 18 ++++++--------- src/components/Username/index.tsx | 14 +++--------- src/i18n/locales/en.ts | 1 + src/lib/url.ts | 30 ++++++++++++++++++++++++- src/services/relay-selection.service.ts | 5 ++--- 8 files changed, 92 insertions(+), 31 deletions(-) diff --git a/src/PageManager.tsx b/src/PageManager.tsx index 34ae472..47c6c1c 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -11,6 +11,7 @@ import WalletPage from '@/pages/secondary/WalletPage' import PostSettingsPage from '@/pages/secondary/PostSettingsPage' import GeneralSettingsPage from '@/pages/secondary/GeneralSettingsPage' import TranslationPage from '@/pages/secondary/TranslationPage' +import SecondaryProfilePage from '@/pages/secondary/ProfilePage' import { CurrentRelaysProvider } from '@/providers/CurrentRelaysProvider' import { NotificationProvider } from '@/providers/NotificationProvider' import { useUserPreferences } from '@/providers/UserPreferencesProvider' @@ -158,6 +159,27 @@ export function useSmartRelayNavigation() { return { navigateToRelay } } +// Custom hook for intelligent profile navigation +export function useSmartProfileNavigation() { + const { showRecommendedRelaysPanel } = useUserPreferences() + const { push: pushSecondary } = useSecondaryPage() + const { setPrimaryNoteView } = usePrimaryNoteView() + + const navigateToProfile = (url: string) => { + if (!showRecommendedRelaysPanel) { + // When right panel is hidden, show profile in primary area + // Extract profile ID from URL (e.g., "/users/npub1..." -> "npub1...") + const profileId = url.replace('/users/', '') + setPrimaryNoteView(, 'settings') + } else { + // Normal behavior - use secondary navigation + pushSecondary(url) + } + } + + return { navigateToProfile } +} + // Custom hook for intelligent settings navigation export function useSmartSettingsNavigation() { const { showRecommendedRelaysPanel } = useUserPreferences() diff --git a/src/components/Note/Highlight/index.tsx b/src/components/Note/Highlight/index.tsx index a6977ac..2693213 100644 --- a/src/components/Note/Highlight/index.tsx +++ b/src/components/Note/Highlight/index.tsx @@ -1,4 +1,4 @@ -import { SecondaryPageLink } from '@/PageManager' +import { useSmartNoteNavigation } from '@/PageManager' import { Event } from 'nostr-tools' import { ExternalLink, Highlighter } from 'lucide-react' import { useTranslation } from 'react-i18next' @@ -13,6 +13,7 @@ export default function Highlight({ className?: string }) { const { t } = useTranslation() + const { navigateToNote } = useSmartNoteNavigation() try { @@ -132,15 +133,18 @@ export default function Highlight({ ) : ( - { + e.stopPropagation() + navigateToNote(toNote(source.bech32)) + }} + className="text-blue-500 hover:underline font-mono cursor-pointer" > {source.type === 'event' ? `note1${source.bech32.substring(5, 13)}...` : `naddr1${source.bech32.substring(6, 14)}...` } - + )} )} diff --git a/src/components/ProfileCard/index.tsx b/src/components/ProfileCard/index.tsx index 6d9b631..b9eadc6 100644 --- a/src/components/ProfileCard/index.tsx +++ b/src/components/ProfileCard/index.tsx @@ -1,4 +1,9 @@ +import { Button } from '@/components/ui/button' import { useFetchProfile } from '@/hooks' +import { toProfile } from '@/lib/link' +import { useSmartProfileNavigation } from '@/PageManager' +import { UserRound } from 'lucide-react' +import { useTranslation } from 'react-i18next' import FollowButton from '../FollowButton' import Nip05 from '../Nip05' import ProfileAbout from '../ProfileAbout' @@ -7,6 +12,8 @@ import { SimpleUserAvatar } from '../UserAvatar' export default function ProfileCard({ pubkey }: { pubkey: string }) { const { profile } = useFetchProfile(pubkey) const { username, about } = profile || {} + const { navigateToProfile } = useSmartProfileNavigation() + const { t } = useTranslation() return (
@@ -24,6 +31,18 @@ export default function ProfileCard({ pubkey }: { pubkey: string }) { className="text-sm text-wrap break-words w-full overflow-hidden text-ellipsis line-clamp-6" /> )} +
) } diff --git a/src/components/UserAvatar/index.tsx b/src/components/UserAvatar/index.tsx index 187beb6..dd679a0 100644 --- a/src/components/UserAvatar/index.tsx +++ b/src/components/UserAvatar/index.tsx @@ -2,10 +2,8 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card' import { Skeleton } from '@/components/ui/skeleton' import { useFetchProfile } from '@/hooks' -import { toProfile } from '@/lib/link' import { generateImageByPubkey } from '@/lib/pubkey' import { cn } from '@/lib/utils' -import { SecondaryPageLink } from '@/PageManager' import { useMemo } from 'react' import ProfileCard from '../ProfileCard' @@ -44,15 +42,13 @@ export default function UserAvatar({ return ( - - e.stopPropagation()}> - - - - {pubkey} - - - + + + + + {pubkey} + + diff --git a/src/components/Username/index.tsx b/src/components/Username/index.tsx index 918ca7d..8ce3f0b 100644 --- a/src/components/Username/index.tsx +++ b/src/components/Username/index.tsx @@ -1,9 +1,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card' import { Skeleton } from '@/components/ui/skeleton' import { useFetchProfile } from '@/hooks' -import { toProfile } from '@/lib/link' import { cn } from '@/lib/utils' -import { SecondaryPageLink } from '@/PageManager' import ProfileCard from '../ProfileCard' export default function Username({ @@ -34,15 +32,9 @@ export default function Username({ return ( -
- e.stopPropagation()} - > - {showAt && '@'} - {username} - +
+ {showAt && '@'} + {username}
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 2d4caa8..bf71de4 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -45,6 +45,7 @@ export default { 'Copy event ID': 'Copy event ID', 'Copy user ID': 'Copy user ID', 'View raw event': 'View raw event', + 'View full profile': 'View full profile', Like: 'Like', 'switch to light theme': 'switch to light theme', 'switch to dark theme': 'switch to dark theme', diff --git a/src/lib/url.ts b/src/lib/url.ts index 9f92929..be4ff1b 100644 --- a/src/lib/url.ts +++ b/src/lib/url.ts @@ -12,7 +12,21 @@ export function normalizeUrl(url: string): string { url = 'wss://' + url } } + + // Parse the URL first to validate it const p = new URL(url) + + // Check if URL has query parameters or hash fragments that suggest it's not a relay + // Relay URLs shouldn't have query params like ?token= or hash fragments + const hasQueryParams = url.includes('?') + const hasHashFragment = url.includes('#') + + // Block URLs with query params or hash fragments (these are likely not relays) + if (hasQueryParams || hasHashFragment) { + console.warn('Skipping URL with query/hash (not a relay):', url) + return '' + } + p.pathname = p.pathname.replace(/\/+/g, '/') if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1) if (p.protocol === 'https:') { @@ -21,6 +35,12 @@ export function normalizeUrl(url: string): string { p.protocol = 'ws:' } + // After protocol normalization, validate it's actually a websocket URL + if (!isWebsocketUrl(p.toString())) { + console.warn('Skipping non-websocket URL:', url) + return '' + } + // Normalize localhost and local network addresses to always use ws:// instead of wss:// // This fixes the common typo where people use wss:// for local relays if (isLocalNetworkUrl(p.toString())) { @@ -32,7 +52,15 @@ export function normalizeUrl(url: string): string { } p.searchParams.sort() p.hash = '' - return p.toString() + + // Final validation: ensure we have a proper websocket URL + const finalUrl = p.toString() + if (!isWebsocketUrl(finalUrl)) { + console.warn('Normalization resulted in invalid websocket URL:', finalUrl) + return '' + } + + return finalUrl } catch { console.error('Invalid URL:', url) return '' diff --git a/src/services/relay-selection.service.ts b/src/services/relay-selection.service.ts index 7da5baf..a005004 100644 --- a/src/services/relay-selection.service.ts +++ b/src/services/relay-selection.service.ts @@ -86,9 +86,8 @@ class RelaySelectionService { if (normalized) { selectableRelays.add(normalized) } else { - // If normalization fails, add the original URL but log a warning - console.warn('Failed to normalize relay URL:', url) - selectableRelays.add(url) + // If normalization fails or returns empty (invalid URL), skip it + console.warn('Skipping invalid relay URL:', url) } }