feat(profile): add round avatar with upload and cropping
- backend: user_avatar_objects binding (PG+SQLite), GC preserves avatar blobs, avatar service (EXIF-stripping WebP 128/512), public avatar read route - web: avatar upload/crop on profile, round avatar in header/members/comments/author - android: avatar upload/crop, round avatar on profile and albums header - openspec: add-user-avatar proposal/design/specs/tasks
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# Spec Delta
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Сборка мусора по ссылкам
|
||||
Объект хранилища SHALL удаляться только когда на него не ссылается ни одно фото и ни один
|
||||
аватар пользователя; удаление альбома и попадание фото в корзину SHALL сохранять объекты.
|
||||
|
||||
#### Scenario: Последняя ссылка удалена
|
||||
- **WHEN** фото удаляется навсегда и больше ни одно фото не ссылается на его объекты
|
||||
- **THEN** объекты удаляются из хранилища
|
||||
|
||||
#### Scenario: Остались ссылки
|
||||
- **WHEN** на объект ссылается другое фото
|
||||
- **THEN** объект сохраняется в хранилище
|
||||
|
||||
#### Scenario: Удаление альбома
|
||||
- **WHEN** владелец удаляет альбом
|
||||
- **THEN** объекты хранилища не удаляются
|
||||
|
||||
#### Scenario: Ссылка аватара
|
||||
- **WHEN** на объект ссылается аватар пользователя
|
||||
- **THEN** объект сохраняется в хранилище, пока ссылка существует
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Аватар пользователя как объект журнала
|
||||
Аватар пользователя SHALL храниться как объект журнала, привязанный к пользователю по ролям,
|
||||
соответствующим нарезкам аватара. Замена и удаление аватара SHALL менять привязку, а объекты
|
||||
без ссылок SHALL освобождаться сборкой мусора.
|
||||
|
||||
#### Scenario: Привязка аватара
|
||||
- **WHEN** пользователь устанавливает аватар
|
||||
- **THEN** объекты нарезок привязываются к пользователю по своим ролям
|
||||
|
||||
#### Scenario: Замена аватара
|
||||
- **WHEN** пользователь заменяет аватар
|
||||
- **THEN** привязка указывает на новые объекты, а старые освобождаются сборкой мусора
|
||||
|
||||
#### Scenario: Удаление аватара
|
||||
- **WHEN** пользователь удаляет аватар
|
||||
- **THEN** привязка снимается, и объекты аватара освобождаются сборкой мусора
|
||||
@@ -0,0 +1,89 @@
|
||||
# Spec Delta
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Загрузка фото профиля с выбором области
|
||||
Пользователь SHALL загружать собственное фото в профиль, выбирая видимую квадратную область.
|
||||
Сохранённый аватар SHALL соответствовать выбранной области. Загрузка SHALL быть доступна только
|
||||
владельцу профиля.
|
||||
|
||||
#### Scenario: Успешная загрузка
|
||||
- **WHEN** пользователь выбирает изображение и подтверждает квадратную область
|
||||
- **THEN** аватар обновляется и возвращается в профиле
|
||||
|
||||
#### Scenario: Выбранная область учтена
|
||||
- **WHEN** пользователь сдвигает или масштабирует область перед подтверждением
|
||||
- **THEN** сохранённый аватар показывает именно выбранную область
|
||||
|
||||
#### Scenario: Не изображение
|
||||
- **WHEN** загруженный файл не является декодируемым изображением
|
||||
- **THEN** система отклоняет запрос и не меняет аватар
|
||||
|
||||
### Requirement: Нормализация аватара и снятие метаданных
|
||||
Система SHALL сохранять аватар в виде квадратных изображений фиксированных размеров и SHALL NOT
|
||||
сохранять EXIF-метаданные загруженного файла.
|
||||
|
||||
#### Scenario: Метаданные не сохраняются
|
||||
- **WHEN** загружено изображение с EXIF, включая GPS-координаты
|
||||
- **THEN** сохранённый аватар не содержит EXIF и GPS
|
||||
|
||||
#### Scenario: Слишком большой файл
|
||||
- **WHEN** размер загруженного файла превышает допустимый предел
|
||||
- **THEN** система отклоняет запрос и не меняет аватар
|
||||
|
||||
### Requirement: Отдача аватара по стабильному URL
|
||||
Профиль и публичные представления пользователя SHALL содержать URL аватара, когда он задан.
|
||||
URL SHALL быть стабильным и SHALL изменяться при обновлении аватара, чтобы клиентские кэши
|
||||
инвалидировались. Изображение SHALL отдаваться по публичной ссылке без аутентификации, чтобы
|
||||
его могли загружать теги изображений и клиентские загрузчики.
|
||||
|
||||
#### Scenario: Ссылка в представлениях пользователя
|
||||
- **WHEN** аутентифицированный клиент запрашивает профиль, участников альбома, комментарии или автора фото
|
||||
- **THEN** представления содержат URL аватара, если он задан
|
||||
|
||||
#### Scenario: Кэш инвалидируется при замене
|
||||
- **WHEN** пользователь заменяет аватар
|
||||
- **THEN** URL аватара изменяется
|
||||
|
||||
#### Scenario: Загрузка изображения без заголовка авторизации
|
||||
- **WHEN** браузер или клиентский загрузчик изображений запрашивает изображение аватара по URL без заголовка авторизации
|
||||
- **THEN** система отдаёт изображение
|
||||
|
||||
### Requirement: Круглое отображение аватара в веб-интерфейсе
|
||||
Веб-приложение SHALL отображать аватар пользователя круглым в шапке, на странице профиля, у
|
||||
участников альбома, в комментариях и у автора фото.
|
||||
|
||||
#### Scenario: Аватар в шапке
|
||||
- **WHEN** аутентифицированный пользователь открывает любое место приложения
|
||||
- **THEN** в шапке отображается его круглый аватар
|
||||
|
||||
#### Scenario: Аватар рядом с другими пользователями
|
||||
- **WHEN** пользователь просматривает участников, комментарии или автора фото
|
||||
- **THEN** аватары этих пользователей отображаются круглыми
|
||||
|
||||
### Requirement: Круглое отображение аватара в Android-клиенте
|
||||
Android-приложение SHALL отображать аватар пользователя круглым на экране профиля и в шапке
|
||||
списка альбомов.
|
||||
|
||||
#### Scenario: Аватар на экране профиля
|
||||
- **WHEN** пользователь открывает экран профиля
|
||||
- **THEN** отображается его круглый аватар
|
||||
|
||||
#### Scenario: Аватар в шапке списка альбомов
|
||||
- **WHEN** пользователь открывает список альбомов
|
||||
- **THEN** в шапке отображается его круглый аватар
|
||||
|
||||
### Requirement: Заглушка при отсутствии аватара
|
||||
Когда аватар не задан, система SHALL показывать нейтральную заглушку-силуэт вместо изображения.
|
||||
|
||||
#### Scenario: Аватар не задан
|
||||
- **WHEN** у пользователя нет аватара и его представление отображается в вебе или Android
|
||||
- **THEN** вместо изображения показывается нейтральная круглая заглушка-силуэт
|
||||
|
||||
### Requirement: Удаление аватара
|
||||
Пользователь SHALL удалять свой аватар; после удаления SHALL показываться заглушка, а связанные
|
||||
объекты хранилища SHALL освобождаться.
|
||||
|
||||
#### Scenario: Сброс аватара
|
||||
- **WHEN** пользователь удаляет аватар
|
||||
- **THEN** представления возвращаются к заглушке, а объекты старого аватара освобождаются
|
||||
Reference in New Issue
Block a user