e6f3d2bcf8009d317180c4d3aa5ee5854c5899e2
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e62b72b5be |
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>
|
||
|
|
f7a849ddcb |
chore(client): translate all user-visible strings to English
Mixed-language UI was confusing — onboarding said "Why DChain / How it
works / Your keys" in English headings but feature descriptions and
CTAs were in Russian; compose's confirm dialog was Russian; feed tabs
were Russian; error messages in humanizeTxError were Russian.
Everything user-facing is now English.
Files touched (only string literals, not comments):
app/index.tsx onboarding slides + CTA buttons
app/(app)/compose.tsx composer alerts, header button, placeholder,
attachment-size hint
app/(app)/feed/index.tsx tab labels (Following/For you/Trending),
empty-state hints, retry button
app/(app)/feed/[id].tsx post detail header + stats rows (Views,
Likes, Size, Paid to publish, Hosted on,
Hashtags)
app/(app)/feed/tag/[tag].tsx empty-state copy
app/(app)/profile/[address].tsx Profile header, Follow/Following,
Edit, Open chat, Address, Copied, Encryption,
Added, Members, unknown-contact hint
app/(app)/new-contact.tsx Search title, placeholder, Search button,
empty-state hint, E2E-ready indicator,
Intro label + placeholder, fee-tier labels
(Min / Standard / Priority), Send request,
Insufficient-balance alert, Request-sent
alert
app/(app)/requests.tsx Notifications title, empty-state, Accept /
Decline buttons, decline-confirm alert,
"wants to add you" line
components/SearchBar.tsx default placeholder
components/feed/PostCard.tsx long-press menu (Delete post, confirm,
Actions / Cancel)
components/feed/ShareSheet.tsx sheet title, contact-search placeholder,
empty state, Select contacts / Send button,
plural helper rewritten for English
components/chat/PostRefCard.tsx "POST" ribbon, "photo" indicator
lib/api.ts humanizeTxError (rate-limit, clock skew,
bad signature, 400/5xx/network-error
messages)
lib/dates.ts dateBucket now returns Today/Yesterday/
"Jun 17, 2025"; month array switched to
English short forms
Code comments left in Russian intentionally — they're developer
context, not user-facing. This commit is purely display-string.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c5ca7a0612 |
feat(feed): image previews + inline header + 5-line truncation + drop comments
Server
node/api_feed.go: new GET /feed/post/{id}/attachment route. Returns
raw attachment bytes with the correct Content-Type so React Native's
<Image source={uri}> can stream them directly without the client
fetching + decoding base64 from the main /feed/post/{id} JSON (would
blow up memory on a 40-post timeline). Respects on-chain soft-delete
(410 when tombstoned). Cache-Control: public, max-age=3600, immutable
— attachments are content-addressed so aggressive caching is safe.
PostCard — rewritten header row
- Avatar + name + time collapsed into a single Pressable row with
flexDirection:'row'. Name gets flexShrink:1 + numberOfLines:1 so
long handles truncate with "…" mid-row instead of pushing the time
onto a second line. Time and separator dot both numberOfLines:1
with no flex — they never shrink, so "2h" stays readable.
- Whole header is one tap target → navigates to the author's profile.
PostCard — body truncation
- Timeline view (compact=false): numberOfLines={5} + ellipsizeMode:
'tail'. Long posts collapse to 5 lines with "…"; tapping the card
opens the detail view where the full body is shown.
- Detail view (compact=true): no line cap — full text, then full-
size attachment below.
PostCard — real image previews
- <Image source={{ uri: `${node}/feed/post/${id}/attachment` }}>
(feed layout).
- Timeline: aspectRatio: 4/5 + resizeMode:'cover' — portrait photos
get cropped so one tall image can't eat the whole feed.
- Detail: aspectRatio: 1 + resizeMode:'contain' so the full image
fits in its original proportions (crop-free).
PostCard — comments button removed
v2.0.0 doesn't implement replies; a dead button with label "0" was
noise. Action row now has 3 cells: heart (with live like count),
eye (views), share (pinned right). Spacing stays balanced because
each of the first two cells is still flex:1.
Post detail screen
- Passes compact prop so the PostCard above renders in full-body /
full-attachment mode.
- Dropped the old AttachmentPreview placeholder — PostCard now
handles images in both modes.
Tests
- go test ./... — all 7 packages green (blockchain / consensus /
identity / media / node / relay / vm).
- tsc --noEmit on client-app — 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
51bc0a1850 |
fix(feed): card spacing, action-row distribution, tab strip, detail inset
- PostCard rows got cramped paddings and a near-invisible divider. Increased paddingTop 12→16, paddingBottom 12→18, paddingHorizontal 14→16; divider colour #141414→#222222 so the seam between posts is legible on OLED blacks. - Action row (chat / ❤ / view / share) used a fixed gap:32 + spacer. Reworked to four flex:1 cells with justifyContent: space-between, so the first three icons distribute evenly across the row and share pins to the right edge. Matches Twitter's layout where each action occupies a quarter of the row regardless of label width. - Feed tab strip (Подписки / Для вас / В тренде) used flex:1 + gap:10 which bunched the three labels together visually. Switched to justifyContent: space-between + paddingHorizontal:20 so each tab hugs its label and the three labels spread to the edges with full horizontal breathing room. - Post detail screen (/feed/[id]) and hashtag feed (/feed/tag/[tag]) were missing the safe-area top inset — their headers butted right against the status bar / notch. Added useSafeAreaInsets().top as paddingTop on the outer View, matching the rest of the app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5b64ef2560 |
feat(client): Twitter-style social feed UI (Phase C of v2.0.0)
Ships the client side of the v2.0.0 feed feature. Folds client-app/
into the monorepo (was previously .gitignored as "tracked separately"
but no separate repo ever existed — for v2.0.0 the client is
first-class).
Feed screens
app/(app)/feed.tsx — Feed tab
- Three-way tab strip: Подписки / Для вас / В тренде backed by
/feed/timeline, /feed/foryou, /feed/trending respectively
- Default landing tab is "Для вас" — surfaces discovery without
requiring the user to follow anyone first
- FlatList with pull-to-refresh + viewability-driven view counter
bump (posts visible ≥ 60% for ≥ 1s trigger POST /feed/post/…/view)
- Floating blue compose button → /compose
- Per-post liked_by_me fetched in batches of 6 after list load
app/(app)/compose.tsx — post composer modal
- Fullscreen, Twitter-like header (✕ left, Опубликовать right)
- Auto-focused multiline TextInput, 4000 char cap
- Hashtag preview chips that auto-update as you type
- expo-image-picker + expo-image-manipulator pipeline: resize to
1080px max-dim, JPEG Q=50 (client-side first-pass compression
before the mandatory server-side scrub)
- Live fee estimate + balance guard with a confirmation modal
("Опубликовать пост? Цена: 0.00X T · Размер: N KB")
- Exif: false passed to ImagePicker as an extra privacy layer
app/(app)/feed/[id].tsx — post detail
- Full PostCard rendering + detailed info panel (views, likes,
size, fee, hosting relay, hashtags as tappable chips)
- Triggers bumpView on mount
- 410 (on-chain soft-delete) routes back to the feed
app/(app)/feed/tag/[tag].tsx — hashtag feed
app/(app)/profile/[address].tsx — rebuilt
- Twitter-ish profile: avatar, name, address short-form, post count
- Posts | Инфо tab strip
- Follow / Unfollow button for non-self profiles (optimistic UI)
- Edit button on self profile → settings
- Secondary actions (chat, copy address) when viewing a known contact
Supporting library
lib/feed.ts — HTTP wrappers + tx builders for every /feed/* endpoint:
- publishPost (POST /feed/publish, signed)
- publishAndCommit (publish → on-chain CREATE_POST)
- fetchPost / fetchStats / bumpView
- fetchAuthorPosts / fetchTimeline / fetchForYou / fetchTrending /
fetchHashtag
- buildCreatePostTx / buildDeletePostTx
- buildFollowTx / buildUnfollowTx
- buildLikePostTx / buildUnlikePostTx
- likePost / unlikePost / followUser / unfollowUser / deletePost
(high-level helpers that bundle build + submitTx)
- formatFee, formatRelativeTime, formatCount — Twitter-like display
helpers
components/feed/PostCard.tsx — core card component
- Memoised for performance (N-row re-render on every like elsewhere
would cost a lot otherwise)
- Optimistic like toggle with heart-bounce spring animation
- Hashtag highlighting in body text (tappable → hashtag feed)
- Long-press context menu (Delete, owner-only)
- Views / likes / share-link / reply icons in footer row
Navigation cleanup
- NavBar: removed the SOON pill on the Feed tab (it's shipped now)
- (app)/_layout: hide NavBar on /compose and /feed/* sub-routes
- AnimatedSlot: treat /feed/<id>, /feed/tag/<t>, /compose as
sub-routes so back-swipe-right closes them
Channel removal (client side)
- lib/types.ts: ContactKind stripped to 'direct' | 'group'; legacy
'channel' flag removed. `kind` field kept for backward compat with
existing AsyncStorage records.
- lib/devSeed.ts: dropped the 5 channel seed contacts.
- components/ChatTile.tsx: removed channel kindIcon branch.
Dependencies
- expo-image-manipulator added for client-side image compression.
- expo-file-system/legacy used for readAsStringAsync (SDK 54 moved
that API to the legacy sub-path; the new streaming API isn't yet
stable).
Type check
- npx tsc --noEmit — clean, 0 errors.
Next (not in this commit)
- Direct attachment-bytes endpoint on the server so post-detail can
actually render the image (currently shows placeholder with URL)
- Cross-relay body fetch via /api/relays + hosting_relay pubkey
- Mentions (@username) with notifications
- Full-text search
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|