Files
dchain/client-app/components/chat/PostRefCard.tsx
vsecoder 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>
2026-04-18 23:39:38 +03:00

144 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* PostRefCard — renders a shared feed post inside a chat bubble.
*
* Visually distinct from plain messages so the user sees at-a-glance
* that this came from the feed, not a direct-typed text. Matches
* VK's "shared wall post" embed pattern:
*
* [newspaper icon] ПОСТ
* @author · 2 строки excerpt'а
* [📷 Фото in this post]
*
* Tap → /(app)/feed/{postID}. The full post (with image + stats +
* like button) is displayed in the standard post-detail screen.
*/
import React from 'react';
import { View, Text, Pressable } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';
import { useStore } from '@/lib/store';
import { Avatar } from '@/components/Avatar';
export interface PostRefCardProps {
postID: string;
author: string;
excerpt: string;
hasImage?: boolean;
/** True when the card appears inside the sender's own bubble (our own
* share). Adjusts colour contrast so it reads on the blue bubble
* background. */
own: boolean;
}
function shortAddr(a: string, n = 6): string {
if (!a) return '—';
return a.length <= n * 2 + 1 ? a : `${a.slice(0, n)}${a.slice(-n)}`;
}
export function PostRefCard({ postID, author, excerpt, hasImage, own }: PostRefCardProps) {
const contacts = useStore(s => s.contacts);
// Resolve author name the same way the feed does.
const contact = contacts.find(c => c.address === author);
const displayName = contact?.username
? `@${contact.username}`
: contact?.alias ?? shortAddr(author);
const onOpen = () => {
router.push(`/(app)/feed/${postID}` as never);
};
// Tinted palette based on bubble side — inside an "own" (blue) bubble
// the card uses a deeper blue so it reads as a distinct nested block,
// otherwise we use the standard card colours.
const bg = own ? 'rgba(0, 0, 0, 0.22)' : '#0a0a0a';
const border = own ? 'rgba(255, 255, 255, 0.15)' : '#1f1f1f';
const labelColor = own ? 'rgba(255, 255, 255, 0.75)' : '#1d9bf0';
const bodyColor = own ? '#ffffff' : '#ffffff';
const subColor = own ? 'rgba(255, 255, 255, 0.65)' : '#8b8b8b';
return (
<Pressable
onPress={onOpen}
style={({ pressed }) => ({
marginBottom: 6,
borderRadius: 14,
backgroundColor: pressed ? 'rgba(0,0,0,0.35)' : bg,
borderWidth: 1,
borderColor: border,
overflow: 'hidden',
})}
>
{/* Top ribbon: "ПОСТ" label — makes the shared nature unmistakable. */}
<View
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 6,
paddingHorizontal: 10,
paddingTop: 8,
paddingBottom: 4,
}}
>
<Ionicons name="newspaper-outline" size={11} color={labelColor} />
<Text
style={{
color: labelColor,
fontSize: 10,
fontWeight: '700',
letterSpacing: 1.2,
}}
>
POST
</Text>
</View>
{/* Author + excerpt */}
<View style={{ flexDirection: 'row', paddingHorizontal: 10, paddingBottom: 10 }}>
<Avatar name={displayName} address={author} size={28} />
<View style={{ flex: 1, marginLeft: 8, minWidth: 0, overflow: 'hidden' }}>
<Text
numberOfLines={1}
style={{
color: bodyColor,
fontWeight: '700',
fontSize: 13,
}}
>
{displayName}
</Text>
{excerpt.length > 0 && (
<Text
numberOfLines={3}
style={{
color: subColor,
fontSize: 12,
lineHeight: 16,
marginTop: 2,
}}
>
{excerpt}
</Text>
)}
{hasImage && (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 4,
marginTop: 6,
}}
>
<Ionicons name="image-outline" size={11} color={subColor} />
<Text style={{ color: subColor, fontSize: 11 }}>
photo
</Text>
</View>
)}
</View>
</View>
</Pressable>
);
}