Three related UX fixes on the client.
1. Participants count on profile
DMs always have exactly two participants (you and the contact) so a
"Участников: 1" row was confusing — either it's obviously the other
person or it's wrong depending on how you count. Removed for direct
conversations; the row still appears for group chats (and shows an
em-dash until v2.1.0 gives groups a real member list).
2. Dev feed seed now activates on network / 404 errors
The seed was only surfaced when the real API returned an EMPTY
array. If the node was down (Network request failed) or the endpoint
replied 404, the catch block quietly set posts to [] and the list
stayed blank — defeating the point of the seed. Now both the empty-
response path AND the network-error path fall back to getDevSeedFeed(),
so scrolling / like-toggling works even without a running node.
Also made the __DEV__ lookup more defensive: use `globalThis.__DEV__`
at runtime instead of the typed global. Some bundler configurations
have the TS type but not the runtime binding, or vice-versa — the
runtime lookup always agrees with Metro.
3. Back from profile → previous screen instead of tab root
Root cause: AnimatedSlot rendered <Slot>, which is stack-less. When
/chats/xyz pushed /profile/abc (cross-group), the chats group
unmounted. Hitting Back then re-entered chats at its root (/chats
list) rather than /chats/xyz.
Replaced <Slot> with <Stack> in AnimatedSlot. Tab switching still
stays flat because NavBar uses router.replace (which maps to
navigation.replace on the Stack — no history accumulation).
Cross-group pushes (post author tap from feed, avatar tap from chat
header, compose modal) now live in the outer Stack's history, so
Back pops correctly to the caller.
The nested Stacks (chats/_layout.tsx, feed/_layout.tsx,
profile/_layout.tsx) still handle intra-group navigation as before.
The PanResponder-based swipe-right-to-back was removed since the
native Stack now provides iOS edge-swipe natively; Android uses the
system back button.
animation: 'none' keeps the visual swap instant — matches the prior
Slot look so nothing flashes slide-animations that weren't there
before. Sub-group layouts can opt into slide_from_right themselves
(profile/_layout.tsx and feed/_layout.tsx already do).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Profile screen
- Missing safe-area top inset on the header — fixed by wrapping the
outer View in paddingTop: insets.top (matches the rest of the tab
screens).
- "Чат" button icon + text sat on two lines because the button used
column layout by default. Rewritten as a full-width CTA pill with
flexDirection: row and alignItems: center → chat-bubble icon and
label sit on one line.
- Dedicated "Копировать адрес" button removed. The address row in
the info card is now the tap target: pressing it copies to clipboard
and flips the row to "Скопировано" with a green check for 1.8s.
- Posts tab removed entirely. User's right — a "chat profile" has no
posts concept, just participant count + date + encryption. The
profile screen is now a single-view info card (address, encryption
status, added date, participants). Posts are discoverable via the
Feed tab; a dedicated /feed/author/{pub} screen is on the roadmap
for browsing a specific user's timeline.
Back-stack navigation
- app/(app)/profile/_layout.tsx + app/(app)/feed/_layout.tsx added,
each with a native <Stack>. The outer AnimatedSlot is stack-less
(intentional — it animates tab switches), so without these nested
stacks `router.back()` from a profile screen had no history to pop
and fell through to root. Now tapping an author in the feed → back
returns to feed; opening a profile from chat header → back returns
to the chat.
Dev feed seed
- lib/devSeedFeed.ts: 12 synthetic posts (text-only, mix of hashtags,
one with has_attachment, varying likes/views, timestamps from "1m
ago" to "36h ago"). Exposed via getDevSeedFeed() — stripped from
production via __DEV__ gate.
- Feed screen falls back to the dev seed only when the real API
returned zero posts. Gives something to scroll / tap / like-toggle
before the backend has real content; real data takes over as soon
as it arrives.
Note: tapping a mock post into detail will 404 against the real node
(the post_ids don't exist on-chain). Intentional — the seed is for
scroll + interaction feel, not deep linking.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>