mirror of
https://github.com/immich-app/immich.git
synced 2025-01-17 01:06:46 +01:00
refactor(web): websocket events (#7152)
This commit is contained in:
parent
bbf7a54c65
commit
c84c0bae6c
10 changed files with 134 additions and 99 deletions
1
web/package-lock.json
generated
1
web/package-lock.json
generated
|
@ -29,6 +29,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.0.0",
|
"@faker-js/faker": "^8.0.0",
|
||||||
"@floating-ui/dom": "^1.5.1",
|
"@floating-ui/dom": "^1.5.1",
|
||||||
|
"@socket.io/component-emitter": "^3.1.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"@sveltejs/kit": "^2.0.6",
|
"@sveltejs/kit": "^2.0.6",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.0.0",
|
"@faker-js/faker": "^8.0.0",
|
||||||
"@floating-ui/dom": "^1.5.1",
|
"@floating-ui/dom": "^1.5.1",
|
||||||
|
"@socket.io/component-emitter": "^3.1.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"@sveltejs/kit": "^2.0.6",
|
"@sveltejs/kit": "^2.0.6",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/server-config.store';
|
import { featureFlags } from '$lib/stores/server-config.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { websocketStore } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
|
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
|
||||||
import { getAssetFilename } from '$lib/utils/asset-utils';
|
import { getAssetFilename } from '$lib/utils/asset-utils';
|
||||||
import { autoGrowHeight } from '$lib/utils/autogrow';
|
import { autoGrowHeight } from '$lib/utils/autogrow';
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
mdiPencil,
|
mdiPencil,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import { asByteUnitString } from '../../utils/byte-units';
|
import { asByteUnitString } from '../../utils/byte-units';
|
||||||
import { handleError } from '../../utils/handle-error';
|
import { handleError } from '../../utils/handle-error';
|
||||||
|
@ -91,14 +91,12 @@
|
||||||
$: people = asset.people || [];
|
$: people = asset.people || [];
|
||||||
$: showingHiddenPeople = false;
|
$: showingHiddenPeople = false;
|
||||||
|
|
||||||
const unsubscribe = websocketStore.onAssetUpdate.subscribe((assetUpdate) => {
|
onMount(() => {
|
||||||
if (assetUpdate && assetUpdate.id === asset.id) {
|
return websocketEvents.on('on_asset_update', (assetUpdate) => {
|
||||||
asset = assetUpdate;
|
if (assetUpdate.id === asset.id) {
|
||||||
}
|
asset = assetUpdate;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
onDestroy(() => {
|
|
||||||
unsubscribe();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||||
import { websocketStore } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
||||||
|
@ -49,32 +49,12 @@
|
||||||
let loaderLoadingDoneTimeout: NodeJS.Timeout;
|
let loaderLoadingDoneTimeout: NodeJS.Timeout;
|
||||||
let automaticRefreshTimeout: NodeJS.Timeout;
|
let automaticRefreshTimeout: NodeJS.Timeout;
|
||||||
|
|
||||||
const { onPersonThumbnail } = websocketStore;
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
close: void;
|
close: void;
|
||||||
refresh: void;
|
refresh: void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Reset value
|
async function loadPeople() {
|
||||||
$onPersonThumbnail = '';
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if ($onPersonThumbnail) {
|
|
||||||
numberOfAssetFaceGenerated.push($onPersonThumbnail);
|
|
||||||
if (
|
|
||||||
isEqual(numberOfAssetFaceGenerated, numberOfPersonToCreate) &&
|
|
||||||
loaderLoadingDoneTimeout &&
|
|
||||||
automaticRefreshTimeout &&
|
|
||||||
selectedPersonToCreate.filter((person) => person !== null).length === numberOfPersonToCreate.length
|
|
||||||
) {
|
|
||||||
clearTimeout(loaderLoadingDoneTimeout);
|
|
||||||
clearTimeout(automaticRefreshTimeout);
|
|
||||||
dispatch('refresh');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
|
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
|
||||||
try {
|
try {
|
||||||
const { people } = await getAllPeople({ withHidden: true });
|
const { people } = await getAllPeople({ withHidden: true });
|
||||||
|
@ -88,6 +68,25 @@
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
isShowLoadingPeople = false;
|
isShowLoadingPeople = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPersonThumbnail = (personId: string) => {
|
||||||
|
numberOfAssetFaceGenerated.push(personId);
|
||||||
|
if (
|
||||||
|
isEqual(numberOfAssetFaceGenerated, numberOfPersonToCreate) &&
|
||||||
|
loaderLoadingDoneTimeout &&
|
||||||
|
automaticRefreshTimeout &&
|
||||||
|
selectedPersonToCreate.filter((person) => person !== null).length === numberOfPersonToCreate.length
|
||||||
|
) {
|
||||||
|
clearTimeout(loaderLoadingDoneTimeout);
|
||||||
|
clearTimeout(automaticRefreshTimeout);
|
||||||
|
dispatch('refresh');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadPeople();
|
||||||
|
return websocketEvents.on('on_person_thumbnail', onPersonThumbnail);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isEqual = (a: string[], b: string[]): boolean => {
|
const isEqual = (a: string[], b: string[]): boolean => {
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { websocketStore } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import type { AssetStore } from '$lib/stores/assets.store';
|
import type { AssetStore } from '$lib/stores/assets.store';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
export let assetStore: AssetStore | null;
|
export let assetStore: AssetStore | null;
|
||||||
|
|
||||||
websocketStore.onAssetUpdate.subscribe((asset) => {
|
onMount(() => {
|
||||||
if (asset && asset.originalFileName && assetStore) {
|
return websocketEvents.on('on_asset_update', (asset) => {
|
||||||
assetStore.updateAsset(asset, true);
|
if (asset.originalFileName && assetStore) {
|
||||||
|
assetStore.updateAsset(asset, true);
|
||||||
|
|
||||||
assetStore.removeAsset(asset.id); // Update timeline
|
assetStore.removeAsset(asset.id); // Update timeline
|
||||||
assetStore.addAsset(asset);
|
assetStore.addAsset(asset);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
|
|
||||||
let showModal = false;
|
let showModal = false;
|
||||||
|
|
||||||
const { onRelease } = websocketStore;
|
const { release } = websocketStore;
|
||||||
|
|
||||||
const semverToName = ({ major, minor, patch }: ServerVersionResponseDto) => `v${major}.${minor}.${patch}`;
|
const semverToName = ({ major, minor, patch }: ServerVersionResponseDto) => `v${major}.${minor}.${patch}`;
|
||||||
|
|
||||||
$: releaseVersion = $onRelease && semverToName($onRelease.releaseVersion);
|
$: releaseVersion = $release && semverToName($release.releaseVersion);
|
||||||
$: serverVersion = $onRelease && semverToName($onRelease.serverVersion);
|
$: serverVersion = $release && semverToName($release.serverVersion);
|
||||||
$: $onRelease?.isAvailable && handleRelease();
|
$: $release?.isAvailable && handleRelease();
|
||||||
|
|
||||||
const onAcknowledge = () => {
|
const onAcknowledge = () => {
|
||||||
localStorage.setItem('appVersion', releaseVersion);
|
localStorage.setItem('appVersion', releaseVersion);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { throttle } from 'lodash-es';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { writable, type Unsubscriber } from 'svelte/store';
|
import { writable, type Unsubscriber } from 'svelte/store';
|
||||||
import { handleError } from '../utils/handle-error';
|
import { handleError } from '../utils/handle-error';
|
||||||
import { websocketStore } from './websocket';
|
import { websocketEvents } from './websocket';
|
||||||
|
|
||||||
export enum BucketPosition {
|
export enum BucketPosition {
|
||||||
Above = 'above',
|
Above = 'above',
|
||||||
|
@ -96,22 +96,14 @@ export class AssetStore {
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
this.unsubscribers.push(
|
this.unsubscribers.push(
|
||||||
websocketStore.onUploadSuccess.subscribe((value) => {
|
websocketEvents.on('on_upload_success', (asset) => {
|
||||||
if (value) {
|
this.addPendingChanges({ type: 'add', value: asset });
|
||||||
this.addPendingChanges({ type: 'add', value });
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
websocketEvents.on('on_asset_trash', (ids) => {
|
||||||
websocketStore.onAssetTrash.subscribe((ids) => {
|
this.addPendingChanges(...ids.map((id): TrashAsset => ({ type: 'trash', value: id })));
|
||||||
if (ids) {
|
|
||||||
this.addPendingChanges(...ids.map((id) => ({ type: 'trash', value: id }) as PendingChange));
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
websocketEvents.on('on_asset_delete', (id: string) => {
|
||||||
websocketStore.onAssetDelete.subscribe((value) => {
|
this.addPendingChanges({ type: 'delete', value: id });
|
||||||
if (value) {
|
|
||||||
this.addPendingChanges({ type: 'delete', value });
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { createEventEmitter } from '$lib/utils/eventemitter';
|
||||||
import type { AssetResponseDto, ServerVersionResponseDto } from '@immich/sdk';
|
import type { AssetResponseDto, ServerVersionResponseDto } from '@immich/sdk';
|
||||||
import { io, type Socket } from 'socket.io-client';
|
import { io, type Socket } from 'socket.io-client';
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import { loadConfig } from './server-config.store';
|
|
||||||
import { user } from './user.store';
|
import { user } from './user.store';
|
||||||
|
|
||||||
export interface ReleaseEvent {
|
export interface ReleaseEvent {
|
||||||
|
@ -10,58 +10,54 @@ export interface ReleaseEvent {
|
||||||
serverVersion: ServerVersionResponseDto;
|
serverVersion: ServerVersionResponseDto;
|
||||||
releaseVersion: ServerVersionResponseDto;
|
releaseVersion: ServerVersionResponseDto;
|
||||||
}
|
}
|
||||||
|
export interface Events {
|
||||||
|
on_upload_success: (asset: AssetResponseDto) => void;
|
||||||
|
on_asset_delete: (assetId: string) => void;
|
||||||
|
on_asset_trash: (assetIds: string[]) => void;
|
||||||
|
on_asset_update: (asset: AssetResponseDto) => void;
|
||||||
|
on_asset_hidden: (assetId: string) => void;
|
||||||
|
on_asset_restore: (assetIds: string[]) => void;
|
||||||
|
on_person_thumbnail: (personId: string) => void;
|
||||||
|
on_server_version: (serverVersion: ServerVersionResponseDto) => void;
|
||||||
|
on_config_update: () => void;
|
||||||
|
on_new_release: (newRelase: ReleaseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const websocket: Socket<Events> = io('', {
|
||||||
|
path: '/api/socket.io',
|
||||||
|
transports: ['websocket'],
|
||||||
|
reconnection: true,
|
||||||
|
forceNew: true,
|
||||||
|
autoConnect: false,
|
||||||
|
});
|
||||||
|
|
||||||
export const websocketStore = {
|
export const websocketStore = {
|
||||||
onUploadSuccess: writable<AssetResponseDto>(),
|
|
||||||
onAssetDelete: writable<string>(),
|
|
||||||
onAssetTrash: writable<string[]>(),
|
|
||||||
onAssetUpdate: writable<AssetResponseDto>(),
|
|
||||||
onPersonThumbnail: writable<string>(),
|
|
||||||
serverVersion: writable<ServerVersionResponseDto>(),
|
|
||||||
connected: writable<boolean>(false),
|
connected: writable<boolean>(false),
|
||||||
onRelease: writable<ReleaseEvent>(),
|
serverVersion: writable<ServerVersionResponseDto>(),
|
||||||
|
release: writable<ReleaseEvent>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let websocket: Socket | null = null;
|
export const websocketEvents = createEventEmitter(websocket);
|
||||||
|
|
||||||
|
websocket
|
||||||
|
.on('connect', () => websocketStore.connected.set(true))
|
||||||
|
.on('disconnect', () => websocketStore.connected.set(false))
|
||||||
|
.on('on_server_version', (serverVersion) => websocketStore.serverVersion.set(serverVersion))
|
||||||
|
.on('on_new_release', (releaseVersion) => websocketStore.release.set(releaseVersion))
|
||||||
|
.on('connect_error', (e) => console.log('Websocket Connect Error', e));
|
||||||
|
|
||||||
export const openWebsocketConnection = async () => {
|
export const openWebsocketConnection = async () => {
|
||||||
try {
|
try {
|
||||||
if (websocket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!get(user)) {
|
if (!get(user)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket = io('', {
|
websocket.connect();
|
||||||
path: '/api/socket.io',
|
|
||||||
transports: ['websocket'],
|
|
||||||
reconnection: true,
|
|
||||||
forceNew: true,
|
|
||||||
autoConnect: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
websocket
|
|
||||||
.on('connect', () => websocketStore.connected.set(true))
|
|
||||||
.on('disconnect', () => websocketStore.connected.set(false))
|
|
||||||
// .on('on_upload_success', (data) => websocketStore.onUploadSuccess.set(data))
|
|
||||||
.on('on_asset_delete', (data) => websocketStore.onAssetDelete.set(data))
|
|
||||||
.on('on_asset_trash', (data) => websocketStore.onAssetTrash.set(data))
|
|
||||||
.on('on_asset_update', (data) => websocketStore.onAssetUpdate.set(data))
|
|
||||||
.on('on_person_thumbnail', (data) => websocketStore.onPersonThumbnail.set(data))
|
|
||||||
.on('on_server_version', (data) => websocketStore.serverVersion.set(data))
|
|
||||||
.on('on_config_update', () => loadConfig())
|
|
||||||
.on('on_new_release', (data) => websocketStore.onRelease.set(data))
|
|
||||||
.on('error', (e) => console.log('Websocket Error', e));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Cannot connect to websocket', error);
|
console.log('Cannot connect to websocket', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const closeWebsocketConnection = () => {
|
export const closeWebsocketConnection = () => {
|
||||||
if (websocket) {
|
websocket.disconnect();
|
||||||
websocket.close();
|
|
||||||
}
|
|
||||||
websocket = null;
|
|
||||||
};
|
};
|
||||||
|
|
42
web/src/lib/utils/eventemitter.ts
Normal file
42
web/src/lib/utils/eventemitter.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import type {
|
||||||
|
DefaultEventsMap,
|
||||||
|
EventsMap,
|
||||||
|
ReservedOrUserEventNames,
|
||||||
|
ReservedOrUserListener,
|
||||||
|
} from '@socket.io/component-emitter';
|
||||||
|
import type { Socket } from 'socket.io-client';
|
||||||
|
|
||||||
|
export function createEventEmitter<
|
||||||
|
ListenEvents extends EventsMap = DefaultEventsMap,
|
||||||
|
EmitEvents extends EventsMap = ListenEvents,
|
||||||
|
ReservedEvents extends EventsMap = NonNullable<unknown>,
|
||||||
|
>(socket: Socket<ListenEvents, EmitEvents>) {
|
||||||
|
function on<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
|
||||||
|
ev: Ev,
|
||||||
|
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>,
|
||||||
|
) {
|
||||||
|
socket.on(ev, listener);
|
||||||
|
return () => {
|
||||||
|
socket.off(ev, listener);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
|
@ -30,7 +30,7 @@
|
||||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { AssetStore } from '$lib/stores/assets.store';
|
import { AssetStore } from '$lib/stores/assets.store';
|
||||||
import { websocketStore } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { clickOutside } from '$lib/utils/click-outside';
|
import { clickOutside } from '$lib/utils/click-outside';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
@ -68,7 +68,6 @@
|
||||||
});
|
});
|
||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
const { selectedAssets, isMultiSelectState } = assetInteractionStore;
|
const { selectedAssets, isMultiSelectState } = assetInteractionStore;
|
||||||
const { onPersonThumbnail } = websocketStore;
|
|
||||||
|
|
||||||
let viewMode: ViewMode = ViewMode.VIEW_ASSETS;
|
let viewMode: ViewMode = ViewMode.VIEW_ASSETS;
|
||||||
let isEditingName = false;
|
let isEditingName = false;
|
||||||
|
@ -119,8 +118,6 @@
|
||||||
|
|
||||||
$: isAllArchive = [...$selectedAssets].every((asset) => asset.isArchived);
|
$: isAllArchive = [...$selectedAssets].every((asset) => asset.isArchived);
|
||||||
$: isAllFavorite = [...$selectedAssets].every((asset) => asset.isFavorite);
|
$: isAllFavorite = [...$selectedAssets].every((asset) => asset.isFavorite);
|
||||||
$: $onPersonThumbnail === data.person.id &&
|
|
||||||
(thumbnailData = getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`);
|
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (people) {
|
if (people) {
|
||||||
|
@ -138,6 +135,12 @@
|
||||||
if (action == 'merge') {
|
if (action == 'merge') {
|
||||||
viewMode = ViewMode.MERGE_PEOPLE;
|
viewMode = ViewMode.MERGE_PEOPLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return websocketEvents.on('on_person_thumbnail', (personId: string) => {
|
||||||
|
if (data.person.id === personId) {
|
||||||
|
thumbnailData = getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleKeyboardPress = (event: KeyboardEvent) => {
|
const handleKeyboardPress = (event: KeyboardEvent) => {
|
||||||
|
|
Loading…
Reference in a new issue