mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor: event manager (#25481)
* refactor: event manager * fix: broken downloadFile endpoint
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
};
|
||||
|
||||
const props: Props = $props();
|
||||
const unsubscribes: Array<() => void> = [];
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribes: Array<() => void> = [];
|
||||
|
||||
for (const name of Object.keys(props)) {
|
||||
const event = name.slice(2) as keyof Events;
|
||||
const listener = props[name as keyof Props] as EventCallback<Events, typeof event> | undefined;
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
};
|
||||
|
||||
const props: Props = $props();
|
||||
const unsubscribes: Array<() => void> = [];
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribes: Array<() => void> = [];
|
||||
|
||||
for (const name of Object.keys(props)) {
|
||||
const event = name.slice(2) as keyof Events;
|
||||
const listener = props[name as keyof Props];
|
||||
@@ -20,8 +21,7 @@
|
||||
|
||||
const args = [event, listener as (...args: Events[typeof event]) => void] as const;
|
||||
|
||||
eventManager.on(...args);
|
||||
unsubscribes.push(() => eventManager.off(...args));
|
||||
unsubscribes.push(eventManager.on(...args));
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ThemeSetting } from '$lib/managers/theme-manager.svelte';
|
||||
import type { ReleaseEvent } from '$lib/types';
|
||||
import { BaseEventManager } from '$lib/utils/base-event-manager.svelte';
|
||||
import type { TreeNode } from '$lib/utils/tree-utils';
|
||||
import type {
|
||||
AlbumResponseDto,
|
||||
@@ -85,54 +86,4 @@ export type Events = {
|
||||
ReleaseEvent: [ReleaseEvent];
|
||||
};
|
||||
|
||||
type Listener<EventMap extends Record<string, unknown[]>, K extends keyof EventMap> = (...params: EventMap[K]) => void;
|
||||
|
||||
class EventManager<EventMap extends Record<string, unknown[]>> {
|
||||
private listeners: {
|
||||
[K in keyof EventMap]?: {
|
||||
listener: Listener<EventMap, K>;
|
||||
once?: boolean;
|
||||
}[];
|
||||
} = {};
|
||||
|
||||
on<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void) {
|
||||
return this.addListener(key, listener, false);
|
||||
}
|
||||
|
||||
once<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void) {
|
||||
return this.addListener(key, listener, true);
|
||||
}
|
||||
|
||||
off<K extends keyof EventMap>(key: K, listener: Listener<EventMap, K>) {
|
||||
if (this.listeners[key]) {
|
||||
this.listeners[key] = this.listeners[key].filter((item) => item.listener !== listener);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
emit<T extends keyof EventMap>(key: T, ...params: EventMap[T]) {
|
||||
if (!this.listeners[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const { listener } of this.listeners[key]) {
|
||||
listener(...params);
|
||||
}
|
||||
|
||||
// remove one time listeners
|
||||
this.listeners[key] = this.listeners[key].filter((item) => !item.once);
|
||||
}
|
||||
|
||||
private addListener<T extends keyof EventMap>(key: T, listener: (...params: EventMap[T]) => void, once: boolean) {
|
||||
if (!this.listeners[key]) {
|
||||
this.listeners[key] = [];
|
||||
}
|
||||
|
||||
this.listeners[key].push({ listener, once });
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export const eventManager = new EventManager<Events>();
|
||||
export const eventManager = new BaseEventManager<Events>();
|
||||
|
||||
@@ -19,7 +19,7 @@ export class QueueManager {
|
||||
}
|
||||
|
||||
constructor() {
|
||||
eventManager.on('QueueUpdate', () => void this.refresh());
|
||||
eventManager.on('QueueUpdate', () => this.refresh());
|
||||
}
|
||||
|
||||
listen() {
|
||||
|
||||
@@ -5,7 +5,7 @@ class ServerConfigManager {
|
||||
#value?: ServerConfigDto = $state();
|
||||
|
||||
constructor() {
|
||||
eventManager.on('SystemConfigUpdate', () => void this.loadServerConfig());
|
||||
eventManager.on('SystemConfigUpdate', () => this.loadServerConfig());
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
||||
@@ -113,9 +113,7 @@ export class TimelineManager extends VirtualScrollManager {
|
||||
|
||||
const onAssetUpdate = (asset: AssetResponseDto) => this.upsertAssets([toTimelineAsset(asset)]);
|
||||
|
||||
eventManager.on('AssetUpdate', onAssetUpdate);
|
||||
|
||||
this.#unsubscribes.push(() => eventManager.off('AssetUpdate', onAssetUpdate));
|
||||
this.#unsubscribes.push(eventManager.on('AssetUpdate', onAssetUpdate));
|
||||
}
|
||||
|
||||
override get scrollTop(): number {
|
||||
|
||||
@@ -6,7 +6,8 @@ class UploadManager {
|
||||
mediaTypes = $state<ServerMediaTypesResponseDto>({ image: [], sidecar: [], video: [] });
|
||||
|
||||
constructor() {
|
||||
eventManager.on('AppInit', () => void this.#loadExtensions()).on('AuthLogout', () => void this.reset());
|
||||
eventManager.on('AppInit', () => this.#loadExtensions());
|
||||
eventManager.on('AuthLogout', () => this.reset());
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
@@ -24,7 +24,7 @@ class MemoryStoreSvelte {
|
||||
|
||||
constructor() {
|
||||
eventManager.on('AuthLogout', () => this.clearCache());
|
||||
eventManager.on('AuthUserLoaded', () => void this.initialize());
|
||||
eventManager.on('AuthUserLoaded', () => this.initialize());
|
||||
}
|
||||
|
||||
ready() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getNotifications, updateNotification, updateNotifications, type NotificationDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -9,7 +8,7 @@ class NotificationStore {
|
||||
notifications = $state<NotificationDto[]>([]);
|
||||
|
||||
constructor() {
|
||||
eventManager.on('AuthLogin', () => handlePromiseError(this.refresh()));
|
||||
eventManager.on('AuthLogin', () => this.refresh());
|
||||
eventManager.on('AuthLogout', () => this.clear());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
type BaseEvents = Record<string, unknown[]>;
|
||||
type EventMap = Record<string, unknown[]>;
|
||||
type PromiseLike<T> = Promise<T> | T;
|
||||
|
||||
export type EventCallback<Events extends BaseEvents, T extends keyof Events> = (
|
||||
...args: Events[T]
|
||||
) => Promise<void> | void;
|
||||
export type EventItem<Events extends BaseEvents, T extends keyof Events = keyof Events> = {
|
||||
export type EventCallback<E extends EventMap, T extends keyof E> = (...args: E[T]) => PromiseLike<unknown>;
|
||||
export type EventItem<E extends EventMap, T extends keyof E = keyof E> = {
|
||||
id: number;
|
||||
event: T;
|
||||
callback: EventCallback<Events, T>;
|
||||
callback: EventCallback<E, T>;
|
||||
};
|
||||
|
||||
let count = 1;
|
||||
@@ -14,7 +13,7 @@ const nextId = () => count++;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export class BaseEventManager<Events extends BaseEvents> {
|
||||
export class BaseEventManager<Events extends EventMap> {
|
||||
#callbacks: EventItem<Events>[] = $state([]);
|
||||
|
||||
on<T extends keyof Events>(event: T, callback?: EventCallback<Events, T>) {
|
||||
|
||||
@@ -21,22 +21,5 @@ export function createEventEmitter<
|
||||
};
|
||||
}
|
||||
|
||||
function once<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
|
||||
ev: Ev,
|
||||
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>,
|
||||
) {
|
||||
socket.once(ev, listener);
|
||||
return () => {
|
||||
socket.off(ev, listener);
|
||||
};
|
||||
}
|
||||
|
||||
function off<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
|
||||
ev: Ev,
|
||||
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>,
|
||||
) {
|
||||
socket.off(ev, listener);
|
||||
}
|
||||
|
||||
return { on, once, off };
|
||||
return { on };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user