Files
pipu-music/docs/social.md
T
mrleo1nid 11a16cd023
ci / api / api (push) Canceled after 0s
ci / module boundaries / boundaries (push) Canceled after 0s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Canceled after 11m29s
ci / db / db (push) Canceled after 11m22s
ci / mobile / mobile (Metro-бандл Android) (push) Canceled after 6m43s
ci / recommender / recommender (push) Canceled after 6m47s
ci / ts / ts (packages + web) (push) Canceled after 1s
feat(social): add listening activity, shared rooms and modern chat
2026-10-23 10:45:40 +03:00

11 KiB
Raw Blame History

Друзья, сообщения и уведомления

Социальные функции работают между пользователями одного сервера. В web и desktop открывается раздел «Друзья и общение», на Android — вкладка «Друзья».

Поведение

  • Поиск по логину и отображаемому имени начинается с двух символов, возвращает до 30 человек. Пароли, роли, настройки и другие приватные поля в результат не попадают.
  • Заявка требует явного принятия получателем. Встречная заявка не принимает дружбу автоматически. Можно отменить свою заявку, отклонить входящую или удалить друга.
  • Личные сообщения и отправка музыки доступны только текущим друзьям. Блокировка прекращает дружбу и закрывает общение в обоих направлениях. Разблокировка дружбу не восстанавливает.
  • История сохраняется после удаления друга, но доступна снова только после новой взаимной дружбы. В первой странице чата — последние 50 сообщений; ранние подгружаются отдельно.
  • В сообщении до 4000 символов. Музыкальное вложение — ID трека либо действующая ссылка общего доступа к плейлисту. Отзыв ссылки делает вложенный плейлист недоступным.
  • «Поделиться» открывает выбор: скопировать ссылку, вызвать системную отправку (когда она поддерживается клиентом) или отправить конкретному другу одним нажатием. Отправка другу создаёт сообщение с музыкальным вложением. Повтор после сетевого сбоя использует тот же UUID.

Спокойные уведомления

Четыре типа: входящая заявка, принятие заявки, личное сообщение, музыка. Центр событий и счётчики сохраняются на сервере, отметки прочтения общие для устройств. Нет звуков, автоматических всплывающих окон и запроса разрешений ОС.

Список уведомлений возвращает до 100 событий, сначала непрочитанные. По мере подтверждения новых становятся доступны более старые непрочитанные события. Открытый видимый чат подтверждает сообщения только до последнего полученного номера; более поздние сообщения не теряются. В скрытой вкладке и в фоне Android автоматического подтверждения нет.

Сейчас это уведомления внутри работающего приложения, с обновлением центра каждые 15 секунд и открытого чата каждые 3 секунды. Web использует стандартную приостановку фонового polling React Query; Android останавливает polling при уходе приложения в фон. Уведомления ОС при закрытом клиенте (Web Push/FCM/нативный desktop) в эту реализацию не входят.

Реализация и проверки

Контракт: api/openapi.yaml, операции getSocial, searchPeople, socialAction, getDirectMessages, sendDirectMessage. Модуль API: internal/social. Миграция 00019_social.sql создаёт дружбу, блокировки, сообщения и уведомления. Миграции применяются штатным запуском API.

Мутации пары сериализованы транзакционной advisory-блокировкой PostgreSQL. Отправка сообщения и создание уведомления атомарны. UUID сообщения глобально уникален; повтор с другим содержимым возвращает конфликт. Чужое уведомление нельзя отметить прочитанным. Поиск скрывает заблокированные аккаунты и личные блокировки, включая обратное направление.

Ограничения: до 50 исходящих ожидающих заявок, до 10 новых заявок за минуту, до 30 сообщений одному человеку за минуту; тело социального POST ограничено 32 KiB. Тексты выводятся обычным текстом, вложения формируют только внутренние маршруты каталога.

Проверки: go test ./internal/social, TEST_DATABASE_URL=… go test -tags integration ./internal/social, pnpm --filter @pipu/web test src/App.test.tsx -t 'social|playlist sharing', pnpm --filter @pipu/web test:e2e e2e/social.spec.ts e2e/playlists.spec.ts. Интеграционный сценарий включён в ci-db.

Активность и приватность

В разделе друзей настройка «Показывать мой статус» принимает значения «Никому» (по умолчанию), «Друзьям», «Всем». Сервер проверяет аудиторию, дружбу и блокировки. При «Никому» сведения о текущем прослушивании не сохраняются. Переключение на это значение удаляет активность всех устройств. Публикация имеет последовательный номер и срок жизни 90 секунд; пауза снимает статус, потерянное соединение не оставляет вечный статус.

Карточка показывает трек и контекст: «Моя волна», плейлист или обычное прослушивание. Трек открывается в каталоге. Плейлист доступен для чтения, пока активность видима: этот переход не создаёт постоянной ссылки общего доступа и не раскрывает существующую.

Совместное прослушивание

«Подключиться» отправляет запрос хозяину и оставляет управление у него. «Пригласить на мой стрим» создаёт собственную комнату: приглашённый становится слушателем. Один пользователь состоит в одной комнате; несколько слушателей могут подключиться к хозяину. Настройки приёма запросов/приглашений и автоматического принятия независимы, обе по умолчанию «Никто». Автопринятие применяется только внутри разрешённой аудитории. Дополнительная настройка управляет запуском звука при подключении.

Только исходное устройство хозяина публикует состояние комнаты. Слушатели получают трек, паузу и позицию с поправкой на серверное время каждые 2 секунды. Обычные кнопки управления, очередь и перемотка заблокированы; громкость, реакции и выход доступны. Это синхронизация собственных потоков из каталога с допуском дрейфа 1,5 секунды, а не ретрансляция аудиобайта хозяина. Браузер может потребовать нажатия «Включить звук». После 10 секунд потери связи слушатель ставится на паузу, комната истекает через 30 секунд без хозяина, отсутствующий слушатель удаляется через 60 секунд, запрос действует 2 минуты.

Чат

Web и desktop используют выдвижную панель справа снизу, Android — общую нижнюю панель. Поддерживаются текст и музыка, ответы с цитатой, шесть реакций, редактирование своих сообщений, удаление для всех, поиск по тексту, история, даты, прочтение и индикатор набора. Удаление очищает исходный текст и музыкальное вложение. Черновики разделены по серверу, пользователю и собеседнику. Скрытая панель не отмечает переписку прочитанной. Анимации учитывают системное уменьшение движения. Фото, файлы и голосовые не входят в версию.

Миграции 00020–00022 добавляют активность, комнаты и возможности чата. Уведомления дополнены запросом, приглашением и принятием совместного прослушивания.

Сигнал друзей в рекомендациях

В настройках рекомендаций можно отключить влияние друзей. Используется только их текущая видимая активность с учётом аудитории и блокировок, без скрытой истории. Go передаёт до 30 допустимых треков; Python добавляет до 10 кандидатов, небольшой бонус с учётом выбранного контекста и не более одного кандидата с источником friends на выдачу. Дизлайки, недоступные треки и недавние повторы исключаются прежними фильтрами. При недоступном рекомендателе резервный подбор добавляет не более одного такого трека в каждую вторую выдачу; тематические волны с seed сохраняют свой сценарий.