fix(client): safeBack helper + prevent self-contact-request
1. GO_BACK warning & stuck screens
When a deep link or direct push put the user on /feed/[id],
/profile/[address], /compose, or /settings without any prior stack
entry, tapping the header chevron emitted:
"ERROR The action 'GO_BACK' was not handled by any navigator"
and did nothing — user was stuck.
New helper lib/utils.safeBack(fallback = '/(app)/chats') wraps
router.canGoBack() — when there's history it pops; otherwise it
replace-navigates to a sensible fallback (chats list by default,
'/' for auth screens so we land back at the onboarding).
Applied to every header chevron and back-from-detail flow:
app/(app)/chats/[id], app/(app)/compose, app/(app)/feed/[id]
(header + onDeleted), app/(app)/feed/tag/[tag],
app/(app)/profile/[address], app/(app)/new-contact (header + OK
button on request-sent alert), app/(app)/settings,
app/(auth)/create, app/(auth)/import.
2. Prevent self-contact-request
new-contact.tsx now compares the resolved address against
keyFile.pub_key at two points:
- right after resolveUsername + getIdentity in search() — before
the profile card even renders, so the user doesn't see the
"Send request" CTA for themselves.
- inside sendRequest() as a belt-and-braces guard in case the
check was somehow bypassed.
The search path shows an inline error ("That's you. You can't
send a contact request to yourself."); sendRequest falls back to
an Alert with the same meaning. Both compare case-insensitively
against the pubkey hex so mixed-case pastes work.
Technically the server would still accept a self-request (the
chain stores it under contact_in:<self>:<self>), but it's a dead-
end UX-wise — the user can't chat with themselves — so the client
blocks it preemptively instead of letting users pay the fee for
nothing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
fetchPost, fetchStats, bumpView, formatCount, formatFee,
|
||||
type FeedPostItem, type PostStats,
|
||||
} from '@/lib/feed';
|
||||
import { safeBack } from '@/lib/utils';
|
||||
|
||||
export default function PostDetailScreen() {
|
||||
const insets = useSafeAreaInsets();
|
||||
@@ -71,14 +72,14 @@ export default function PostDetailScreen() {
|
||||
|
||||
const onDeleted = useCallback(() => {
|
||||
// Go back to feed — the post is gone.
|
||||
router.back();
|
||||
safeBack();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: '#000000', paddingTop: insets.top }}>
|
||||
<Header
|
||||
divider
|
||||
left={<IconButton icon="chevron-back" size={36} onPress={() => router.back()} />}
|
||||
left={<IconButton icon="chevron-back" size={36} onPress={() => safeBack()} />}
|
||||
title="Post"
|
||||
/>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IconButton } from '@/components/IconButton';
|
||||
import { PostCard, PostSeparator } from '@/components/feed/PostCard';
|
||||
import { useStore } from '@/lib/store';
|
||||
import { fetchHashtag, fetchStats, type FeedPostItem } from '@/lib/feed';
|
||||
import { safeBack } from '@/lib/utils';
|
||||
|
||||
export default function HashtagScreen() {
|
||||
const insets = useSafeAreaInsets();
|
||||
@@ -80,7 +81,7 @@ export default function HashtagScreen() {
|
||||
<View style={{ flex: 1, backgroundColor: '#000000', paddingTop: insets.top }}>
|
||||
<Header
|
||||
divider
|
||||
left={<IconButton icon="chevron-back" size={36} onPress={() => router.back()} />}
|
||||
left={<IconButton icon="chevron-back" size={36} onPress={() => safeBack()} />}
|
||||
title={`#${tag}`}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user