/** * Dev-only mock posts for the feed. * * Why: in __DEV__ before any real posts exist on the node, the timeline/ * for-you/trending tabs come back empty. Empty state is fine visually but * doesn't let you test scrolling, like animations, view-counter bumps, * navigation to post detail, etc. This module injects a small set of * synthetic posts so the UI has something to chew on. * * Gating: * - Only active when __DEV__ === true (stripped from production builds). * - Only surfaces when the REAL API returns an empty array. If the node * is returning actual posts, we trust those and skip the mocks. * * These posts have made-up post_ids — tapping on them to open detail * WILL 404 against the real backend. That's intentional — the mock is * purely for scroll / tap-feedback testing. */ import type { FeedPostItem } from './feed'; // Fake hex-like pubkeys so Avatar's colour hash still looks varied. function fakeAddr(seed: number): string { const h = (seed * 2654435761).toString(16).padStart(8, '0'); return (h + h + h + h).slice(0, 64); } function fakePostID(n: number): string { return `dev${String(n).padStart(29, '0')}`; } const NOW = Math.floor(Date.now() / 1000); // Small curated pool of posts covering the render surface we care about: // plain text, hashtag variety, different lengths, likes / views spread, // reply/quote references, one with an attachment marker. const SEED_POSTS: FeedPostItem[] = [ { post_id: fakePostID(1), author: fakeAddr(1), content: 'Добро пожаловать в ленту DChain. Это #DEV-посты — они видны только пока реальная лента пустая.', created_at: NOW - 60, size: 200, hosting_relay: fakeAddr(100), views: 127, likes: 42, has_attachment: false, hashtags: ['dev'], }, { post_id: fakePostID(2), author: fakeAddr(2), content: 'Пробую новую ленту #twitter-style. Лайки, просмотры, подписки — всё on-chain, тела постов — off-chain в mailbox релея.', created_at: NOW - 540, size: 310, hosting_relay: fakeAddr(100), views: 89, likes: 23, has_attachment: false, hashtags: ['twitter'], }, { post_id: fakePostID(3), author: fakeAddr(3), content: 'Сжатие изображений — максимальное на клиенте (WebP Q=50 @1080p), плюс серверный EXIF-скраб через stdlib re-encode. GPS-координаты из EXIF больше никогда не утекают. #privacy', created_at: NOW - 1200, size: 420, hosting_relay: fakeAddr(100), views: 312, likes: 78, has_attachment: true, hashtags: ['privacy'], }, { post_id: fakePostID(4), author: fakeAddr(4), content: 'Короткий пост.', created_at: NOW - 3600, size: 128, hosting_relay: fakeAddr(100), views: 12, likes: 3, has_attachment: false, }, { post_id: fakePostID(5), author: fakeAddr(1), content: 'Отвечаю сам себе — фича threads пока через reply_to только, без UI thread-виджета.', created_at: NOW - 7200, size: 220, hosting_relay: fakeAddr(100), views: 45, likes: 11, has_attachment: false, reply_to: fakePostID(1), }, { post_id: fakePostID(6), author: fakeAddr(5), content: '#golang + #badgerdb + #libp2p = DChain бэкенд. Пять package в test suite, все зелёные.', created_at: NOW - 10800, size: 180, hosting_relay: fakeAddr(100), views: 201, likes: 66, has_attachment: false, hashtags: ['golang', 'badgerdb', 'libp2p'], }, { post_id: fakePostID(7), author: fakeAddr(6), content: 'Feed-mailbox хранит тела постов до 30 дней (настраиваемо через DCHAIN_FEED_TTL_DAYS). Потом BadgerDB выселяет автоматически — chain-метаданные остаются навсегда.', created_at: NOW - 14400, size: 380, hosting_relay: fakeAddr(100), views: 156, likes: 48, has_attachment: false, }, { post_id: fakePostID(8), author: fakeAddr(7), content: 'Pricing: BasePostFee = 1000 µT (0.001 T) + 1 µT за каждый байт. Уходит владельцу релея, принявшего пост.', created_at: NOW - 21600, size: 250, hosting_relay: fakeAddr(100), views: 78, likes: 22, has_attachment: false, }, { post_id: fakePostID(9), author: fakeAddr(8), content: 'Twitter-like, но без миллиардов долларов на инфраструктуру — каждый оператор ноды платит за свой кусок хостинга и зарабатывает на публикациях. #decentralised #messaging', created_at: NOW - 43200, size: 340, hosting_relay: fakeAddr(100), views: 412, likes: 103, has_attachment: false, hashtags: ['decentralised', 'messaging'], }, { post_id: fakePostID(10), author: fakeAddr(9), content: 'Короче. Лайк = on-chain tx с fee 1000 µT. Дорого для спама, дёшево для реального лайка. Пока без батчинга, но в плане. #design', created_at: NOW - 64800, size: 200, hosting_relay: fakeAddr(100), views: 92, likes: 29, has_attachment: false, hashtags: ['design'], }, { post_id: fakePostID(11), author: fakeAddr(2), content: 'Follow граф на chain: двусторонний индекс (forward + inbound), так что Followers() и Following() — оба O(M).', created_at: NOW - 86400 - 1000, size: 230, hosting_relay: fakeAddr(100), views: 61, likes: 14, has_attachment: false, }, { post_id: fakePostID(12), author: fakeAddr(10), content: 'Рекомендации (For You): берём последние 48ч постов, фильтруем подписки + уже лайкнутые + свои, ранжируем по likes × 3 + views. Версия 1 — будет умнее. #recsys', created_at: NOW - 129600, size: 290, hosting_relay: fakeAddr(100), views: 189, likes: 58, has_attachment: false, hashtags: ['recsys'], }, ]; /** True when the current build is a Metro dev bundle. __DEV__ is a * global injected by Metro at bundle time and typed via react-native's * ambient declarations, so no ts-ignore is needed. */ function isDev(): boolean { return typeof __DEV__ !== 'undefined' && __DEV__ === true; } /** * Returns the dev-seed post list (only in __DEV__). Called by the Feed * screen as a fallback when the real API returned an empty list. */ export function getDevSeedFeed(): FeedPostItem[] { if (!isDev()) return []; return SEED_POSTS; }