feat(android): обновлять список альбомов в реальном времени

Приложение подписывается на персональный поток GET /api/sse/me и применяет
album.created/updated/deleted к кэшу Room, поэтому список и открытый экран
следуют изменениям с другого устройства или из веба без ручного обновления.

- PersonalSseClient и парсер событий жизненного цикла; подписка живёт вместе с
  сессией (AlbumsViewModel), закрывается на выходе
- AlbumRepository.apply: upsert альбома или удаление его вместе с фото;
  при переподключении список догоняется серверным снимком
- потеря доступа (удаление альбома или исключение участника) убирает альбом из
  списка, а открытый экран возвращается к списку
- README: раздел Live updates и ограничение с истечением токена
This commit is contained in:
iu
2026-10-16 12:47:53 +03:00
parent 44cb7c7d68
commit 9061f7fc44
23 changed files with 906 additions and 13 deletions
+19 -3
View File
@@ -96,9 +96,25 @@ signature rotates. Albums and photo grids open from cache when offline.
## Live updates
While an album screen is active the client holds
`GET /api/sse/albums/:id?token=` and backfills missed changes via
`GET /api/albums/:id/photos?since=` after a reconnect.
While the app is signed in the client holds the personal stream
`GET /api/sse/me` (authorized by the bearer header, not a query token) and applies
album lifecycle events (`album.created`, `album.updated`, `album.deleted`) straight
to the local cache, so the album list and an open album screen follow changes made
elsewhere without a manual refresh. After a reconnect the list is re-synced over
REST, which also drops albums deleted while the stream was down. When an album is
deleted or the user is removed from it, the album leaves the list and the open
album screen returns to it.
While an album screen is active the client also holds `GET /api/sse/albums/:id` and
backfills missed photo changes via `GET /api/albums/:id/photos?since=` after a
reconnect.
The streaming client does not refresh tokens by itself: it is built without the
authenticator that ordinary API calls use, and the authorization interceptor only
reads the current access token. A reconnect after expiry therefore restarts with
whatever token is stored, and recovers once any other request has refreshed it; a
client that idles with only the stream open keeps retrying with the stale token
until the next API call renews it.
## Attributes and actions