feat(realtime): доставлять клиентам события жизненного цикла альбома и изменения состава фото
Публиковать album.created/updated/deleted и изменения состава фото в личный поток SSE, а также photo.created при загрузке, чтобы списки альбомов, счётчики и открытые страницы альбома обновлялись без перезагрузки. - members.Repository получил Audience (владелец + участники) для дешёвого веера - albums.Service рассылает события в личные потоки получателей, перечитывая альбом через GetByID и attachCover, чтобы payload совпадал с GET /api/albums - members.Service публикует при принятии инвайта, смене роли и исключении - photos.Service публикует photo.created при загрузке и album.updated при добавлении/копировании/перемещении/восстановлении/корзине/окончательном удалении - photos_count теперь считает фото вне корзины - web-клиент подписывается на /api/sse/me один раз на уровне layout'а (app) и применяет события к стору альбомов; страница альбома уходит при потере доступа
This commit is contained in:
@@ -5,10 +5,22 @@
|
||||
import AlbumPanel from '$lib/components/AlbumPanel.svelte';
|
||||
import TransferDialog from '$lib/components/TransferDialog.svelte';
|
||||
import AlbumActionsDialogs from '$lib/components/AlbumActionsDialogs.svelte';
|
||||
import { auth } from '$lib/stores/auth.svelte';
|
||||
import { albumLive } from '$lib/stores/album-live';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
const showPanel = $derived($page.url.pathname !== '/');
|
||||
|
||||
// Personal stream: one subscription for the whole session, kept alive across
|
||||
// navigation inside the (app) group. The stream updates the album list
|
||||
// directly from event payloads and re-syncs it after a reconnect.
|
||||
$effect(() => {
|
||||
const token = auth.access;
|
||||
if (!token) return;
|
||||
albumLive.start(token);
|
||||
return () => albumLive.stop();
|
||||
});
|
||||
</script>
|
||||
|
||||
<AppShell panelVisible={showPanel}>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
import { auth } from '$lib/stores/auth.svelte';
|
||||
import { albums } from '$lib/stores/albums.svelte';
|
||||
import { albumAccessLost } from '$lib/albums-events';
|
||||
import { density } from '$lib/stores/density.svelte';
|
||||
import { selection } from '$lib/stores/selection.svelte';
|
||||
import { photoUpdates } from '$lib/stores/photo-updates.svelte';
|
||||
@@ -82,6 +83,15 @@
|
||||
});
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// The personal stream removes albums from the store on deletion or access
|
||||
// loss (member excluded); when the album open here disappears, leave to
|
||||
// the overview instead of showing a stale page.
|
||||
if (!albums.loaded || loading || !album) return;
|
||||
if (!albumAccessLost(albums.list, albumId)) return;
|
||||
goto('/');
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// Patch a photo edited in the panel (e.g. tags) into the grid list so
|
||||
// derived UI like the 360 badge updates without a reload. Read the list
|
||||
|
||||
Reference in New Issue
Block a user