Реализация пересылки сообщений (Forward) в мобильном приложении
Forward проще, чем reply, — никакой рекурсии, нет привязки к позиции в ленте. Но детали съедают время: выбор нескольких чатов сразу, предпросмотр медиа перед отправкой, атрибуция («переслано от Иван»), и обязательный вопрос — копируем вложение или пересылаем ссылку на оригинал.
Модель данных
На сервере пересланное сообщение — новый объект с полем forwarded_from: { message_id, sender_name, sender_id }. Вложения либо копируются (новый объект в хранилище), либо указывают на тот же S3-ключ. Второй вариант дешевле по хранилищу, но создаёт зависимость: удаление оригинального сообщения ломает вложение у всех пересланных копий. Копирование — надёжнее.
Если пересылаем медиа между чатами разных типов (личный → групповой) — бэкенд должен проверить права доступа к оригинальному вложению. Иначе приватные фото из закрытого чата утекут в публичный.
UI: выбор чатов и отправка
Паттерн один: лонг-тап на сообщении открывает контекстное меню, пункт «Переслать» → bottom sheet или модальный экран со списком чатов. Интерфейс аналогичен share-листу iOS (UIActivityViewController), но со своим списком внутри приложения.
На iOS поверх стандартного UITableView со списком чатов добавляем мультиселект через tableView(_:didSelectRowAt:) с хранением выбранных IndexPath в Set<IndexPath>. Кнопка «Отправить (N)» в navbar обновляется через navigationItem.rightBarButtonItem.title. При подтверждении — последовательные POST-запросы или один batch-endpoint.
На Compose — LazyColumn с selectedChats: Set<String> в ViewModel. Каждый элемент ChatItem проверяет item.id in selectedChats и рисует Checkbox или цветной overlay. Кнопка «Отправить» во FloatingActionButton активна при selectedChats.isNotEmpty().
Атрибуция в ленте
В bubble пересланного сообщения отображаем подпись «Переслано от [имя]». Если оригинальный отправитель запретил пересылку (настройка приватности) — скрываем имя, показываем просто «Пересланное сообщение». Проверку делаем на сервере при создании forward: если у оригинального пользователя allow_forwarding = false, в forwarded_from возвращаем null.
Flutter
ForwardCubit держит список чатов и selectedIds. UI — showModalBottomSheet с StatefulBuilder или отдельный route. После выбора — cubit.forwardMessage(messageId, toChats: selectedIds). Анимация закрытия шторки и возврата в чат — стандартная через Navigator.pop.
Сроки
1-3 рабочих дня. Если нужна пересылка между разными типами чатов с разграничением прав — ближе к 3. Стоимость рассчитывается индивидуально.







