diff --git a/src/pages/ContactsPage.tsx b/src/pages/ContactsPage.tsx
index 4e2bb6f..f4ec28f 100644
--- a/src/pages/ContactsPage.tsx
+++ b/src/pages/ContactsPage.tsx
@@ -72,7 +72,7 @@ function ContactRow({
{zapTarget && }
-
+
diff --git a/src/pages/ConversationPage.tsx b/src/pages/ConversationPage.tsx
index b50e29c..0709198 100644
--- a/src/pages/ConversationPage.tsx
+++ b/src/pages/ConversationPage.tsx
@@ -3,12 +3,14 @@ import { useParams, Link, useSearchParams } from 'react-router-dom';
import { useSeoMeta } from '@unhead/react';
import { nip19 } from 'nostr-tools';
import { Layout } from '@/components/Layout';
+import { LoginArea } from '@/components/auth/LoginArea';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Skeleton } from '@/components/ui/skeleton';
import { useCurrentUser } from '@/hooks/useCurrentUser';
+import { useToast } from '@/hooks/useToast';
import { useNostrLogin } from '@nostrify/react/login';
import { hasNip44 as checkNip44 } from '@/lib/signerCapabilities';
import { useConversation, useSendDirectMessage, type DirectMessage } from '@/hooks/useDirectMessages';
@@ -70,6 +72,7 @@ export default function ConversationPage() {
const { npub } = useParams<{ npub: string }>();
const [searchParams] = useSearchParams();
const { user } = useCurrentUser();
+ const { toast } = useToast();
const { logins } = useNostrLogin();
const messagesEndRef = useRef(null);
@@ -132,6 +135,15 @@ export default function ConversationPage() {
onSuccess: () => {
setNewMessage('');
},
+ onError: (error) => {
+ // Without this a failed send is silent (common with interactive
+ // signers / relay hiccups) and the draft just sits there.
+ toast({
+ title: 'Message failed to send',
+ description: error instanceof Error ? error.message : 'Please try again in a moment.',
+ variant: 'destructive',
+ });
+ },
}
);
};
@@ -157,6 +169,9 @@ export default function ConversationPage() {
Log in to view messages
+
+
+
diff --git a/src/pages/DirectMessagesPage.tsx b/src/pages/DirectMessagesPage.tsx
index 909ab63..4075160 100644
--- a/src/pages/DirectMessagesPage.tsx
+++ b/src/pages/DirectMessagesPage.tsx
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
import { useSeoMeta } from '@unhead/react';
import { nip19 } from 'nostr-tools';
import { Layout } from '@/components/Layout';
+import { LoginArea } from '@/components/auth/LoginArea';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
@@ -179,6 +180,9 @@ export default function DirectMessagesPage() {
Private, encrypted messaging with skaters in the community
+
+
+
diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx
index 070fc5a..5c0e56b 100644
--- a/src/pages/SettingsPage.tsx
+++ b/src/pages/SettingsPage.tsx
@@ -1156,7 +1156,15 @@ export default function SettingsPage() {
const [searchParams] = useSearchParams();
// Allow deep-linking into a specific section via ?section=profile etc.
- const initialSection = (searchParams.get('section') as Section) ?? null;
+ // Only sections that actually render are accepted; anything else (e.g. a
+ // stale bookmark to ?section=language) falls back to the menu rather than a
+ // blank screen with no way back.
+ const RENDERABLE_SECTIONS: Section[] = [
+ 'profile', 'security', 'nip05', 'mydata', 'relays', 'crewnetwork', 'appearance', 'wallet', 'advanced',
+ ];
+ const requestedSection = searchParams.get('section') as Section;
+ const initialSection =
+ requestedSection && RENDERABLE_SECTIONS.includes(requestedSection) ? requestedSection : null;
const [section, setSection] = useState(initialSection);
const handleNavigate = (s: Section) => setSection(s);
diff --git a/src/pages/SpotDetailPage.tsx b/src/pages/SpotDetailPage.tsx
index 3be657e..ab96d29 100644
--- a/src/pages/SpotDetailPage.tsx
+++ b/src/pages/SpotDetailPage.tsx
@@ -510,18 +510,25 @@ export default function SpotDetailPage() {
If you own or manage this shop, you can claim it to verify ownership and manage the listing.
{hasNip05 ? (
-
+
+
+ To claim this shop, add a NIP-05 identifier that matches the shop's website domain.
+
)}
diff --git a/src/pages/TourDetailPage.tsx b/src/pages/TourDetailPage.tsx
index 4abac3b..9c24a4e 100644
--- a/src/pages/TourDetailPage.tsx
+++ b/src/pages/TourDetailPage.tsx
@@ -54,9 +54,10 @@ export default function TourDetailPage() {
const handleBookTour = () => {
if (!tour) return;
- // Navigate to messages/DM with the guide
- // For now, navigate to messages page - in future can open DM composer
- navigate(`/messages?to=${tour.pubkey}`);
+ // Open a DM with the guide. Must use the same /messages/:npub route every
+ // other entry point uses — the old `/messages?to=` param was ignored, so
+ // the CTA silently opened an empty inbox.
+ navigate(`/messages/${nip19.npubEncode(tour.pubkey)}`);
};
const handleShare = async () => {