// ─── Key material ──────────────────────────────────────────────────────────── export interface KeyFile { pub_key: string; // hex Ed25519 public key (32 bytes) priv_key: string; // hex Ed25519 private key (64 bytes) x25519_pub: string; // hex X25519 public key (32 bytes) x25519_priv: string; // hex X25519 private key (32 bytes) } // ─── Contact ───────────────────────────────────────────────────────────────── /** * Тип беседы в v2.0.0 — только direct (1:1 E2E чат). Каналы убраны в * пользу публичной ленты (см. lib/feed.ts). Поле `kind` осталось ради * обратной совместимости со старыми записями в AsyncStorage; новые * контакты не пишут его. */ export type ContactKind = 'direct' | 'group'; export interface Contact { address: string; // Ed25519 pubkey hex — blockchain address x25519Pub: string; // X25519 pubkey hex — encryption key username?: string; // @name from registry contract alias?: string; // local nickname addedAt: number; // unix ms /** Legacy field (kept for backward compat with existing AsyncStorage). */ kind?: ContactKind; /** Количество непрочитанных — опционально, проставляется WS read-receipt'ами. */ unread?: number; } // ─── Messages ───────────────────────────────────────────────────────────────── export interface Envelope { /** sha256(nonce||ciphertext)[:16] hex — stable server-assigned id. */ id: string; sender_pub: string; // X25519 hex recipient_pub: string; // X25519 hex nonce: string; // hex 24 bytes ciphertext: string; // hex NaCl box timestamp: number; // unix seconds (server's sent_at, normalised client-side) } /** * Вложение к сообщению. MVP — хранится как URI на локальной файловой * системе клиента (expo-image-picker / expo-document-picker / expo-av * возвращают именно такие URI). Wire-формат для передачи attachment'ов * через relay-envelope ещё не финализирован — пока этот тип для UI'а и * локального отображения. * * Формат по kind: * image — width/height опциональны (image-picker их отдаёт) * video — same + duration в секундах * voice — duration в секундах, нет дизайна превью кроме waveform-stub * file — name + size в байтах, тип через mime */ export type AttachmentKind = 'image' | 'video' | 'voice' | 'file'; export interface Attachment { kind: AttachmentKind; uri: string; // локальный file:// URI или https:// (incoming decoded) mime?: string; // 'image/jpeg', 'application/pdf', … name?: string; // имя файла (для file) size?: number; // байты (для file) width?: number; // image/video height?: number; // image/video duration?: number; // seconds (video/voice) /** Для kind='video' — рендерить как круглое видео-сообщение (Telegram-style). */ circle?: boolean; } export interface Message { id: string; from: string; // X25519 pubkey of sender text: string; timestamp: number; mine: boolean; /** true если сообщение было отредактировано. Показываем "Edited" в углу. */ edited?: boolean; /** * Для mine=true — true если получатель его прочитал. * UI: пустая галочка = отправлено, filled = прочитано. * Для mine=false не используется. */ read?: boolean; /** Одно вложение. Multi-attach пока не поддерживается — будет массивом. */ attachment?: Attachment; /** * Если сообщение — ответ на другое, здесь лежит ссылка + short preview * того оригинала. id используется для scroll-to + highlight; text/author * — для рендера "quoted"-блока внутри текущего bubble'а без запроса * исходного сообщения (копия замороженная в момент ответа). */ replyTo?: { id: string; text: string; author: string; // @username / alias / "you" }; /** * Ссылка на пост из ленты. Если присутствует — сообщение рендерится как * карточка-превью поста (аватар автора, хэндл, текст-excerpt, картинка * если есть). Тап на карточку → открывается полный пост. Сценарий — юзер * нажал Share в ленте и отправил пост в этот чат/ЛС. * * Содержимое (автор, excerpt) дублируется тут, чтобы карточку можно было * рендерить оффлайн / когда у хостящей релей-ноды пропал пост — чат * остаётся читаемым независимо от жизни ленты. */ postRef?: { postID: string; author: string; // Ed25519 hex — для чипа имени в карточке excerpt: string; // первые 120 символов тела поста hasImage?: boolean; }; } // ─── Chat ──────────────────────────────────────────────────────────────────── export interface Chat { contactAddress: string; // Ed25519 pubkey hex contactX25519: string; // X25519 pubkey hex username?: string; alias?: string; lastMessage?: string; lastTime?: number; unread: number; } // ─── Contact request ───────────────────────────────────────────────────────── export interface ContactRequest { from: string; // Ed25519 pubkey hex x25519Pub: string; // X25519 pubkey hex; empty until fetched from identity username?: string; intro: string; // plaintext intro (stored on-chain) timestamp: number; txHash: string; } // ─── Transaction ───────────────────────────────────────────────────────────── export interface TxRecord { hash: string; type: string; from: string; to?: string; amount?: number; fee: number; timestamp: number; status: 'confirmed' | 'pending'; } // ─── Node info ─────────────────────────────────────────────────────────────── export interface NetStats { total_blocks: number; total_txs: number; peer_count: number; chain_id: string; } export interface NodeSettings { nodeUrl: string; contractId: string; // username_registry contract }