Node flags (cmd/node/main.go):
--max-cpu / --max-ram-mb — Go runtime caps (GOMAXPROCS / GOMEMLIMIT)
--feed-disk-limit-mb — hard 507 refusal for new post bodies over quota
--chain-disk-limit-mb — advisory watcher (can't reject blocks without
breaking consensus; logs WARN every minute)
Client — Saved Messages (self-chat):
- Auto-created on sign-in, pinned top of chat list, blue bookmark avatar
- Send short-circuits the relay (no encrypt, no fee, no mailbox hop)
- Empty state rendered outside inverted FlatList — fixes the mirrored
"say hi…" on Android RTL-aware layout builds
- PostCard shows "You" for own posts instead of the self-contact alias
Client — user walls:
- New route /(app)/feed/author/[pub] with infinite-scroll via
`created_at` cursor and pull-to-refresh
- Profile screen gains "View posts" button (universal) next to
"Open chat" (contact-only)
Feed pipeline:
- Bump client JPEG quality 0.5 → 0.75 to match server scrubber (Q=75),
so a 60 KiB compose doesn't balloon past 256 KiB after server re-encode
- ErrPostTooLarge now wraps with the actual size vs cap, errors.Is
preserved in the HTTP layer
- FeedMailbox quota + DiskUsage surface — supports new CLI flag
README:
- Step-by-step "first node / joiner" section on the landing page,
full flag tables incl. the new resource-cap group, minimal
checklists for open/private/low-end deployments
87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
/**
|
||
* Avatar — круглая заглушка с инициалом, опционально online-пип.
|
||
* Нет зависимостей от асинхронных источников (картинок) — для messenger-тайла
|
||
* важнее мгновенный рендер, чем фотография. Если в будущем будут фото,
|
||
* расширяем здесь.
|
||
*/
|
||
import React from 'react';
|
||
import { View, Text } from 'react-native';
|
||
import { Ionicons } from '@expo/vector-icons';
|
||
|
||
export interface AvatarProps {
|
||
/** Имя / @username — берём первый символ для placeholder. */
|
||
name?: string;
|
||
/** Адрес (hex pubkey) — fallback для тех у кого нет имени. */
|
||
address?: string;
|
||
/** Общий размер в px. По умолчанию 48 (tile size). */
|
||
size?: number;
|
||
/** Цвет пипа справа-снизу. undefined = без пипа. */
|
||
dotColor?: string;
|
||
/** Класс для обёртки (position: relative кадр). */
|
||
className?: string;
|
||
/**
|
||
* Saved Messages variant — blue circle with a bookmark glyph, Telegram-style.
|
||
* When set, `name`/`address` are ignored for the visual.
|
||
*/
|
||
saved?: boolean;
|
||
}
|
||
|
||
/** Простое хэширование имени → один из 6 оттенков серого для разнообразия. */
|
||
function pickBg(seed: string): string {
|
||
const shades = ['#1a1a1a', '#222222', '#2a2a2a', '#151515', '#1c1c1c', '#1f1f1f'];
|
||
let h = 0;
|
||
for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) & 0xffff;
|
||
return shades[h % shades.length];
|
||
}
|
||
|
||
export function Avatar({ name, address, size = 48, dotColor, className, saved }: AvatarProps) {
|
||
const seed = (name ?? address ?? '?').replace(/^@/, '');
|
||
const initial = seed.charAt(0).toUpperCase() || '?';
|
||
const bg = saved ? '#1d9bf0' : pickBg(seed);
|
||
|
||
return (
|
||
<View className={className} style={{ width: size, height: size, position: 'relative' }}>
|
||
<View
|
||
style={{
|
||
width: size,
|
||
height: size,
|
||
borderRadius: size / 2,
|
||
backgroundColor: bg,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
}}
|
||
>
|
||
{saved ? (
|
||
<Ionicons name="bookmark" size={size * 0.5} color="#ffffff" />
|
||
) : (
|
||
<Text
|
||
style={{
|
||
color: '#d0d0d0',
|
||
fontSize: size * 0.4,
|
||
fontWeight: '600',
|
||
includeFontPadding: false,
|
||
}}
|
||
>
|
||
{initial}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
{dotColor && (
|
||
<View
|
||
style={{
|
||
position: 'absolute',
|
||
right: 0,
|
||
bottom: 0,
|
||
width: size * 0.28,
|
||
height: size * 0.28,
|
||
borderRadius: size * 0.14,
|
||
backgroundColor: dotColor,
|
||
borderWidth: 2,
|
||
borderColor: '#000',
|
||
}}
|
||
/>
|
||
)}
|
||
</View>
|
||
);
|
||
}
|