mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat: add album create and delete events with websocket integration
This commit is contained in:
parent
cfbc24579d
commit
186cc11692
6 changed files with 122 additions and 5 deletions
|
|
@ -51,6 +51,8 @@ type EventMap = {
|
|||
// album events
|
||||
AlbumUpdate: [{ id: string; recipientId: string }];
|
||||
AlbumInvite: [{ id: string; userId: string }];
|
||||
AlbumDelete: [{ id: string; userId: string }];
|
||||
AlbumCreate: [{ id: string; userId: string }];
|
||||
|
||||
// asset events
|
||||
AssetTag: [{ assetId: string }];
|
||||
|
|
@ -110,6 +112,8 @@ export interface ClientEventMap {
|
|||
on_new_release: [ReleaseNotification];
|
||||
on_notification: [NotificationDto];
|
||||
on_session_delete: [string];
|
||||
on_album_delete: [string];
|
||||
on_album_create: [string];
|
||||
|
||||
AssetUploadReadyV1: [{ asset: SyncAssetV1; exif: SyncAssetExifV1 }];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ export class AlbumService extends BaseService {
|
|||
albumUsers,
|
||||
);
|
||||
|
||||
await this.eventRepository.emit('AlbumCreate', { id: album.id, userId: auth.user.id });
|
||||
|
||||
for (const { userId } of albumUsers) {
|
||||
await this.eventRepository.emit('AlbumInvite', { id: album.id, userId });
|
||||
}
|
||||
|
|
@ -154,6 +156,7 @@ export class AlbumService extends BaseService {
|
|||
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||
await this.requireAccess({ auth, permission: Permission.AlbumDelete, ids: [id] });
|
||||
await this.albumRepository.delete(id);
|
||||
await this.eventRepository.emit('AlbumDelete', { id, userId: auth.user.id });
|
||||
}
|
||||
|
||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||
|
|
|
|||
|
|
@ -211,6 +211,18 @@ export class NotificationService extends BaseService {
|
|||
await this.jobRepository.queue({ name: JobName.NotifyAlbumInvite, data: { id, recipientId: userId } });
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AlbumDelete' })
|
||||
onAlbumDelete({ id, userId }: ArgOf<'AlbumDelete'>) {
|
||||
console.log(`Album ${id} deleted by user ${userId}`);
|
||||
this.eventRepository.clientSend('on_album_delete', userId, id);
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AlbumCreate' })
|
||||
onAlbumCreate({ id, userId }: ArgOf<'AlbumCreate'>) {
|
||||
console.log(`Album ${id} created by user ${userId}`);
|
||||
this.eventRepository.clientSend('on_album_create', userId, id);
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'SessionDelete' })
|
||||
onSessionDelete({ sessionId }: ArgOf<'SessionDelete'>) {
|
||||
// after the response is sent
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
} from '$lib/stores/preferences.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { userInteraction } from '$lib/stores/user.svelte';
|
||||
import { websocketEvents } from '$lib/stores/websocket';
|
||||
import { makeSharedLinkUrl } from '$lib/utils';
|
||||
import {
|
||||
confirmAlbumDelete,
|
||||
|
|
@ -36,11 +37,18 @@
|
|||
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { normalizeSearchString } from '$lib/utils/string-utils';
|
||||
import { addUsersToAlbum, deleteAlbum, isHttpError, type AlbumResponseDto, type AlbumUserAddDto } from '@immich/sdk';
|
||||
import {
|
||||
addUsersToAlbum,
|
||||
deleteAlbum,
|
||||
getAlbumInfo,
|
||||
isHttpError,
|
||||
type AlbumResponseDto,
|
||||
type AlbumUserAddDto,
|
||||
} from '@immich/sdk';
|
||||
import { modalManager } from '@immich/ui';
|
||||
import { mdiDeleteOutline, mdiFolderDownloadOutline, mdiRenameOutline, mdiShareVariantOutline } from '@mdi/js';
|
||||
import { groupBy } from 'lodash-es';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import { onDestroy, onMount, type Snippet } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
|
|
@ -210,6 +218,51 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Handle album deletion via websocket events
|
||||
let unsubscribeWebsocket: (() => void) | undefined;
|
||||
|
||||
onMount(() => {
|
||||
unsubscribeWebsocket = websocketEvents.on('on_album_delete', (albumId) => {
|
||||
// Remove the deleted album from both arrays
|
||||
ownedAlbums = ownedAlbums.filter((album) => album.id !== albumId);
|
||||
sharedAlbums = sharedAlbums.filter((album) => album.id !== albumId);
|
||||
});
|
||||
|
||||
// Add listener for album creation
|
||||
const unsubscribeCreate = websocketEvents.on('on_album_create', async (albumId) => {
|
||||
try {
|
||||
// Fetch the newly created album details
|
||||
const newAlbum = await getAlbumInfo({ id: albumId });
|
||||
|
||||
// Add to owned albums if the current user is the owner
|
||||
if (newAlbum.ownerId === $user?.id) {
|
||||
ownedAlbums = [newAlbum, ...ownedAlbums];
|
||||
} else {
|
||||
// Check if current user is a shared user of this album
|
||||
const isSharedWithCurrentUser = newAlbum.albumUsers.some((albumUser) => albumUser.user.id === $user?.id);
|
||||
if (isSharedWithCurrentUser) {
|
||||
sharedAlbums = [newAlbum, ...sharedAlbums];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch new album details:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Return a combined unsubscribe function
|
||||
const originalUnsubscribe = unsubscribeWebsocket;
|
||||
unsubscribeWebsocket = () => {
|
||||
originalUnsubscribe?.();
|
||||
unsubscribeCreate?.();
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribeWebsocket) {
|
||||
unsubscribeWebsocket();
|
||||
}
|
||||
});
|
||||
|
||||
const showAlbumContextMenu = (contextMenuDetail: ContextMenuPosition, album: AlbumResponseDto) => {
|
||||
contextMenuTargetAlbum = album;
|
||||
contextMenuPosition = {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { userInteraction } from '$lib/stores/user.svelte';
|
||||
import { websocketEvents } from '$lib/stores/websocket';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getAllAlbums, type AlbumResponseDto } from '@immich/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { getAlbumInfo, getAllAlbums, type AlbumResponseDto } from '@immich/sdk';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
let albums: AlbumResponseDto[] = $state([]);
|
||||
let allAlbums: AlbumResponseDto[] = $state([]);
|
||||
|
||||
// Handle album deletion via websocket events
|
||||
let unsubscribeWebsocket: (() => void) | undefined;
|
||||
|
||||
onMount(async () => {
|
||||
if (userInteraction.recentAlbums) {
|
||||
|
|
@ -14,13 +19,51 @@
|
|||
return;
|
||||
}
|
||||
try {
|
||||
const allAlbums = await getAllAlbums({});
|
||||
allAlbums = await getAllAlbums({});
|
||||
albums = allAlbums.sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)).slice(0, 3);
|
||||
userInteraction.recentAlbums = albums;
|
||||
} catch (error) {
|
||||
handleError(error, $t('failed_to_load_assets'));
|
||||
}
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
unsubscribeWebsocket = websocketEvents.on('on_album_delete', (albumId) => {
|
||||
// Remove the deleted album from allAlbums
|
||||
allAlbums = allAlbums.filter((album) => album.id !== albumId);
|
||||
// Update the displayed albums with the filtered and sorted result
|
||||
albums = allAlbums.sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)).slice(0, 3);
|
||||
userInteraction.recentAlbums = albums;
|
||||
});
|
||||
|
||||
// Add listener for album creation
|
||||
const unsubscribeCreate = websocketEvents.on('on_album_create', async (albumId) => {
|
||||
try {
|
||||
// Fetch the newly created album details
|
||||
const newAlbum = await getAlbumInfo({ id: albumId });
|
||||
// Add the new album to allAlbums
|
||||
allAlbums = [newAlbum, ...allAlbums];
|
||||
// Update the displayed albums with the new sorted result
|
||||
albums = allAlbums.sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)).slice(0, 3);
|
||||
userInteraction.recentAlbums = albums;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch new album details:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Return a combined unsubscribe function
|
||||
const originalUnsubscribe = unsubscribeWebsocket;
|
||||
unsubscribeWebsocket = () => {
|
||||
originalUnsubscribe?.();
|
||||
unsubscribeCreate?.();
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribeWebsocket) {
|
||||
unsubscribeWebsocket();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#each albums as album (album.id)}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ export interface Events {
|
|||
on_new_release: (newRelase: ReleaseEvent) => void;
|
||||
on_session_delete: (sessionId: string) => void;
|
||||
on_notification: (notification: NotificationDto) => void;
|
||||
on_album_delete: (albumId: string) => void;
|
||||
on_album_create: (albumId: string) => void;
|
||||
}
|
||||
|
||||
const websocket: Socket<Events> = io({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue