Разработка мобильного приложения для форума
Форум в мобильном приложении — это иерархическая структура: разделы → темы → посты. Главная техническая задача — правильная пагинация в многоуровневых списках, сортировка тем (по активности, по дате, по рейтингу), и отображение вложенных цитат без разрыва читабельности на маленьком экране.
Структура данных
boards (id, slug, name, parent_board_id, position) -- разделы форума
threads (id, board_id, author_id, title, views, replies_count, last_reply_at, is_pinned, is_locked)
posts (id, thread_id, author_id, content_html, quote_post_id, created_at, updated_at, is_deleted)
last_reply_at в threads — ключевое поле для сортировки по активности. Обновляется при каждом новом посте. Индекс (board_id, is_pinned DESC, last_reply_at DESC) — стандартная выборка списка тем.
replies_count — денормализованное поле, инкрементируется через триггер. Не считаем COUNT(*) каждый раз.
Список тем: пагинация и сортировка
На мобиле список тем — UITableView (iOS) или LazyColumn (Android). Сортировки: «Новые», «Активные», «Популярные». Переключение — SegmentedControl или TabRow.
Пагинация cursor-based для «Новых» и «Активных»:
- «Новые»: cursor по
created_at - «Активные»: cursor по
(is_pinned, last_reply_at)— закреплённые всегда первые
Для «Популярных» (по просмотрам или рейтингу за неделю) — offset-пагинация допустима, так как список стабилен.
Закреплённые темы (is_pinned = true) — всегда вверху независимо от сортировки. На iOS через DiffableDataSourceSnapshot с двумя секциями: pinned и regular.
Тред: вложенные посты и цитаты
Посты в теме — плоский список с quote_post_id (не дерево, как Reddit). При цитате показываем вложенный блок:
┌─ Цитата автора X: ┐
│ "Текст цитаты" │
└────────────────────────────────┘
Ответ на цитату...
На iOS: кастомный UIView внутри ячейки поста с backgroundColor(.systemGray6), layer.cornerRadius = 8, layer.borderWidth = 1. Тап на цитату скроллит к оригинальному посту (аналогично reply в чате).
HTML-контент из базы рендерим через WKWebView (для сложного форматирования) или через UILabel с NSAttributedString из NSAttributedString(data:options:[.documentType: NSAttributedString.DocumentType.html]). WKWebView тяжелее, но корректнее рендерит форматирование. В Compose — AndroidView с WebView или HtmlText из Accompanist.
Навигация по разделам
Многоуровневые разделы: boards с parent_board_id. Навигация — стек: главный список разделов → подраздел → список тем → тред.
На iOS: UINavigationController с push. Breadcrumbs вверху — UIScrollView горизонтальный с кнопками «Раздел → Подраздел». На Android: NavHost с NavController, TopAppBar с иконкой назад.
Редактор поста
Минимум: UITextView с поддержкой жирный, курсив, код. Toolbar над клавиатурой с кнопками форматирования — inputAccessoryView на iOS, Composable над TextField на Android с анимацией появления через AnimatedVisibility.
Цитирование при ответе — аналогично reply в чате: выбранный текст или кнопка «Цитировать» под постом вставляет блок цитаты в поле ввода.
Черновик сохраняется локально при каждом изменении (дебаунс 1 сек) — UserDefaults / DataStore.
Поиск
Поиск по форуму: GET /search?q=...&board_id=...&page=.... Full-text поиск через PostgreSQL tsvector/GIN или Elasticsearch. На мобиле: поиск-экран с UISearchBar / SearchBar composable, результаты — темы и отдельные посты, группированные по типу.
Непрочитанные темы и бейджи
Таблица thread_reads (user_id, thread_id, last_read_post_id, read_at). Тема «непрочитана» если last_reply_at > thread_reads.read_at. Количество непрочитанных разделов — бейдж на иконке раздела.
Обновление thread_reads при открытии треда: POST при достижении конца списка или при закрытии экрана — через viewWillDisappear (iOS) / DisposableEffect (Compose).
Этапы работы
Проектирование структуры разделов и схемы БД → API (разделы, темы, посты, поиск) → мобильный UI основных экранов → редактор с форматированием → уведомления о новых постах в подписанных темах → тестирование.
Сроки
MVP (разделы, список тем, тред, простой редактор) — 2-3 недели. Полноценный форум с поиском, непрочитанными, подписками, модерацией — 1-3 месяца. Стоимость рассчитывается индивидуально после анализа требований.







