fix(client): contact-request endpoint path + search screen polish

1. Contact requests silently 404'd
   fetchContactRequests hit /api/relay/contacts, but the server mounts
   the whole /relay/* group at root (no /api prefix). Result: every
   poll returned 404, the catch swallowed it, and the notifications
   tab stayed empty even after the user sent themselves a CONTACT_
   REQUEST on-chain. Fixed the client path to /relay/contacts — same
   pattern as sendEnvelope / fetchInbox in the v1.0.x relay cleanup.

2. Search screen was half-finished
   SearchBar used a dual-state hack (idle-centered Text overlaid with
   an invisible TextInput) that broke focus + alignment on Android and
   sometimes ate taps. Rewrote as a plain single-row pill: icon +
   TextInput + optional clear button. Fewer moving parts, predictable
   focus, proper placeholder styling.

   new-contact.tsx cleaned up:
   - Title "New chat" → "Поиск" (matches the NavBar tab label and the
     rest of the Russian UI).
   - All labels localised: "Accept/Decline", "Intro", "Anti-spam fee",
     fee-tier names, error messages, final CTA.
   - Proper empty-state hint when query is empty (icon + headline +
     explanation of @username / hex / DC prefix) instead of just a
     floating helper text.
   - Search button hidden until user types something — the empty-
     state stands alone, no dead grey button under it.
   - onClear handler on SearchBar resets the resolved profile too.

   requests.tsx localised: title, empty-state, Accept/Decline button
   copy, confirmation Alert text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
vsecoder
2026-04-18 23:19:03 +03:00
parent 3e9ddc1a43
commit 516940fa8e
4 changed files with 115 additions and 111 deletions

View File

@@ -51,7 +51,7 @@ export default function RequestsScreen() {
setRequests(requests.filter(r => r.txHash !== req.txHash));
router.replace(`/(app)/chats/${req.from}` as never);
} catch (e: any) {
Alert.alert('Accept failed', humanizeTxError(e));
Alert.alert('Не удалось принять', humanizeTxError(e));
} finally {
setAccepting(null);
}
@@ -59,12 +59,12 @@ export default function RequestsScreen() {
function decline(req: ContactRequest) {
Alert.alert(
'Decline request',
`Decline request from ${req.username ? '@' + req.username : shortAddr(req.from)}?`,
'Отклонить запрос',
`Отклонить запрос от ${req.username ? '@' + req.username : shortAddr(req.from)}?`,
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Отмена', style: 'cancel' },
{
text: 'Decline',
text: 'Отклонить',
style: 'destructive',
onPress: () => setRequests(requests.filter(r => r.txHash !== req.txHash)),
},
@@ -91,7 +91,7 @@ export default function RequestsScreen() {
{name}
</Text>
<Text style={{ color: '#8b8b8b', fontSize: 12, marginTop: 2 }}>
wants to message you · {relativeTime(req.timestamp)}
хочет добавить вас в контакты · {relativeTime(req.timestamp)}
</Text>
{req.intro ? (
<Text
@@ -123,7 +123,7 @@ export default function RequestsScreen() {
{isAccepting ? (
<ActivityIndicator size="small" color="#ffffff" />
) : (
<Text style={{ color: '#ffffff', fontWeight: '700', fontSize: 13 }}>Accept</Text>
<Text style={{ color: '#ffffff', fontWeight: '700', fontSize: 13 }}>Принять</Text>
)}
</Pressable>
<Pressable
@@ -137,7 +137,7 @@ export default function RequestsScreen() {
borderWidth: 1, borderColor: '#1f1f1f',
})}
>
<Text style={{ color: '#ffffff', fontWeight: '600', fontSize: 13 }}>Decline</Text>
<Text style={{ color: '#ffffff', fontWeight: '600', fontSize: 13 }}>Отклонить</Text>
</Pressable>
</View>
</View>
@@ -147,16 +147,16 @@ export default function RequestsScreen() {
return (
<View style={{ flex: 1, backgroundColor: '#000000', paddingTop: insets.top }}>
<TabHeader title="Notifications" />
<TabHeader title="Уведомления" />
{requests.length === 0 ? (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 32 }}>
<Ionicons name="notifications-outline" size={42} color="#3a3a3a" />
<Text style={{ color: '#ffffff', fontSize: 16, fontWeight: '700', marginTop: 10 }}>
All caught up
Всё прочитано
</Text>
<Text style={{ color: '#8b8b8b', fontSize: 13, textAlign: 'center', marginTop: 6, lineHeight: 19 }}>
Contact requests and network events will appear here.
Запросы на общение и события сети появятся здесь.
</Text>
</View>
) : (