diff --git a/web/src/lib/components/album-page/album-options.svelte b/web/src/lib/components/album-page/album-options.svelte index 84a2873788..ebcf835649 100644 --- a/web/src/lib/components/album-page/album-options.svelte +++ b/web/src/lib/components/album-page/album-options.svelte @@ -2,7 +2,6 @@ import Icon from '$lib/components/elements/icon.svelte'; import { updateAlbumInfo, type AlbumResponseDto, type UserResponseDto, AssetOrder } from '@immich/sdk'; import { mdiArrowDownThin, mdiArrowUpThin, mdiPlus } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; @@ -16,6 +15,9 @@ export let order: AssetOrder | undefined; export let user: UserResponseDto; export let onChangeOrder: (order: AssetOrder) => void; + export let onClose: () => void; + export let onToggleEnabledActivity: () => void; + export let onShowSelectSharedUser: () => void; const options: Record<AssetOrder, RenderedOption> = { [AssetOrder.Asc]: { icon: mdiArrowUpThin, title: $t('oldest_first') }, @@ -24,12 +26,6 @@ $: selectedOption = order ? options[order] : options[AssetOrder.Desc]; - const dispatch = createEventDispatcher<{ - close: void; - toggleEnableActivity: void; - showSelectSharedUser: void; - }>(); - const handleToggle = async (returnedOption: RenderedOption) => { if (selectedOption === returnedOption) { return; @@ -51,7 +47,7 @@ }; </script> -<FullScreenModal title={$t('options')} onClose={() => dispatch('close')}> +<FullScreenModal title={$t('options')} {onClose}> <div class="items-center justify-center"> <div class="py-2"> <h2 class="text-gray text-sm mb-2">{$t('settings').toUpperCase()}</h2> @@ -68,14 +64,14 @@ title={$t('comments_and_likes')} subtitle={$t('let_others_respond')} checked={album.isActivityEnabled} - on:toggle={() => dispatch('toggleEnableActivity')} + onToggle={onToggleEnabledActivity} /> </div> </div> <div class="py-2"> <div class="text-gray text-sm mb-3">{$t('people').toUpperCase()}</div> <div class="p-2"> - <button type="button" class="flex items-center gap-2" on:click={() => dispatch('showSelectSharedUser')}> + <button type="button" class="flex items-center gap-2" on:click={onShowSelectSharedUser}> <div class="rounded-full w-10 h-10 border border-gray-500 flex items-center justify-center"> <div><Icon path={mdiPlus} size="25" /></div> </div> diff --git a/web/src/lib/components/album-page/albums-controls.svelte b/web/src/lib/components/album-page/albums-controls.svelte index ae8178a805..14faee5e7f 100644 --- a/web/src/lib/components/album-page/albums-controls.svelte +++ b/web/src/lib/components/album-page/albums-controls.svelte @@ -154,7 +154,7 @@ title={$t('sort_albums_by')} options={Object.values(sortOptionsMetadata)} selectedOption={selectedSortOption} - on:select={({ detail }) => handleChangeSortBy(detail)} + onSelect={handleChangeSortBy} render={({ id }) => ({ title: albumSortByNames[id], icon: sortIcon, @@ -166,7 +166,7 @@ title={$t('group_albums_by')} options={Object.values(groupOptionsMetadata)} selectedOption={selectedGroupOption} - on:select={({ detail }) => handleChangeGroupBy(detail)} + onSelect={handleChangeGroupBy} render={({ id, isDisabled }) => ({ title: albumGroupByNames[id], icon: groupIcon, diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 5e3499bd10..a13e37c2b5 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -394,13 +394,13 @@ <CreateSharedLinkModal albumId={albumToShare.id} onClose={() => closeShareModal()} - on:created={() => albumToShare && handleSharedLinkCreated(albumToShare)} + onCreated={() => albumToShare && handleSharedLinkCreated(albumToShare)} /> {:else} <UserSelectionModal album={albumToShare} - on:select={({ detail: users }) => handleAddUsers(users)} - on:share={() => (showShareByURLModal = true)} + onSelect={handleAddUsers} + onShare={() => (showShareByURLModal = true)} onClose={() => closeShareModal()} /> {/if} diff --git a/web/src/lib/components/album-page/share-info-modal.svelte b/web/src/lib/components/album-page/share-info-modal.svelte index c9ac224992..ee98d5a821 100644 --- a/web/src/lib/components/album-page/share-info-modal.svelte +++ b/web/src/lib/components/album-page/share-info-modal.svelte @@ -8,7 +8,7 @@ AlbumUserRole, } from '@immich/sdk'; import { mdiDotsVertical } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { handleError } from '../../utils/handle-error'; import ConfirmDialog from '../shared-components/dialog/confirm-dialog.svelte'; import MenuOption from '../shared-components/context-menu/menu-option.svelte'; @@ -20,11 +20,8 @@ export let album: AlbumResponseDto; export let onClose: () => void; - - const dispatch = createEventDispatcher<{ - remove: string; - refreshAlbum: void; - }>(); + export let onRemove: (userId: string) => void; + export let onRefreshAlbum: () => void; let currentUser: UserResponseDto; let selectedRemoveUser: UserResponseDto | null = null; @@ -52,7 +49,7 @@ try { await removeUserFromAlbum({ id: album.id, userId }); - dispatch('remove', userId); + onRemove(userId); const message = userId === 'me' ? $t('album_user_left', { values: { album: album.albumName } }) @@ -71,7 +68,7 @@ const message = $t('user_role_set', { values: { user: user.name, role: role == AlbumUserRole.Viewer ? $t('role_viewer') : $t('role_editor') }, }); - dispatch('refreshAlbum'); + onRefreshAlbum(); notificationController.show({ type: NotificationType.Info, message }); } catch (error) { handleError(error, $t('errors.unable_to_change_album_user_role')); diff --git a/web/src/lib/components/album-page/user-selection-modal.svelte b/web/src/lib/components/album-page/user-selection-modal.svelte index 5521d52173..ee0a5c7410 100644 --- a/web/src/lib/components/album-page/user-selection-modal.svelte +++ b/web/src/lib/components/album-page/user-selection-modal.svelte @@ -13,13 +13,16 @@ type UserResponseDto, } from '@immich/sdk'; import { mdiCheck, mdiEye, mdiLink, mdiPencil, mdiShareCircle } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import UserAvatar from '../shared-components/user-avatar.svelte'; import { t } from 'svelte-i18n'; export let album: AlbumResponseDto; export let onClose: () => void; + export let onSelect: (selectedUsers: AlbumUserAddDto[]) => void; + export let onShare: () => void; + let users: UserResponseDto[] = []; let selectedUsers: Record<string, { user: UserResponseDto; role: AlbumUserRole }> = {}; @@ -29,10 +32,6 @@ { title: $t('remove_user'), value: 'none' }, ]; - const dispatch = createEventDispatcher<{ - select: AlbumUserAddDto[]; - share: void; - }>(); let sharedLinks: SharedLinkResponseDto[] = []; onMount(async () => { await getSharedLinks(); @@ -99,7 +98,7 @@ title={$t('role')} options={roleOptions} render={({ title, icon }) => ({ title, icon })} - on:select={({ detail: { value } }) => handleChangeRole(user, value)} + onSelect={({ value }) => handleChangeRole(user, value)} /> </div> {/key} @@ -152,10 +151,8 @@ rounded="full" disabled={Object.keys(selectedUsers).length === 0} on:click={() => - dispatch( - 'select', - Object.values(selectedUsers).map(({ user, ...rest }) => ({ userId: user.id, ...rest })), - )}>{$t('add')}</Button + onSelect(Object.values(selectedUsers).map(({ user, ...rest }) => ({ userId: user.id, ...rest })))} + >{$t('add')}</Button > </div> {/if} @@ -166,7 +163,7 @@ <button type="button" class="flex flex-col place-content-center place-items-center gap-2 hover:cursor-pointer" - on:click={() => dispatch('share')} + on:click={onShare} > <Icon path={mdiLink} size={24} /> <p class="text-sm">{$t('create_link')}</p> diff --git a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte index 15d3b6accc..cd4e8091af 100644 --- a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte @@ -40,8 +40,8 @@ <Portal target="body"> <AlbumSelectionModal {shared} - on:newAlbum={({ detail }) => handleAddToNewAlbum(detail)} - on:album={({ detail }) => handleAddToAlbum(detail)} + onNewAlbum={handleAddToNewAlbum} + onAlbumClick={handleAddToAlbum} onClose={() => (showSelectionModal = false)} /> </Portal> diff --git a/web/src/lib/components/asset-viewer/actions/delete-action.svelte b/web/src/lib/components/asset-viewer/actions/delete-action.svelte index 1e3cfdd28d..ae5f83c456 100644 --- a/web/src/lib/components/asset-viewer/actions/delete-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/delete-action.svelte @@ -82,6 +82,6 @@ {#if showConfirmModal} <Portal target="body"> - <DeleteAssetDialog size={1} on:cancel={() => (showConfirmModal = false)} on:confirm={() => deleteAsset()} /> + <DeleteAssetDialog size={1} onCancel={() => (showConfirmModal = false)} onConfirm={deleteAsset} /> </Portal> {/if} diff --git a/web/src/lib/components/asset-viewer/activity-status.svelte b/web/src/lib/components/asset-viewer/activity-status.svelte index 8646570fec..fe6ee79363 100644 --- a/web/src/lib/components/asset-viewer/activity-status.svelte +++ b/web/src/lib/components/asset-viewer/activity-status.svelte @@ -2,26 +2,22 @@ import { locale } from '$lib/stores/preferences.store'; import type { ActivityResponseDto } from '@immich/sdk'; import { mdiCommentOutline, mdiHeart, mdiHeartOutline } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import Icon from '../elements/icon.svelte'; export let isLiked: ActivityResponseDto | null; export let numberOfComments: number | undefined; export let disabled: boolean; - - const dispatch = createEventDispatcher<{ - openActivityTab: void; - favorite: void; - }>(); + export let onOpenActivityTab: () => void; + export let onFavorite: () => void; </script> <div class="w-full flex p-4 text-white items-center justify-center rounded-full gap-5 bg-immich-dark-bg bg-opacity-60"> - <button type="button" class={disabled ? 'cursor-not-allowed' : ''} on:click={() => dispatch('favorite')} {disabled}> + <button type="button" class={disabled ? 'cursor-not-allowed' : ''} on:click={onFavorite} {disabled}> <div class="items-center justify-center"> <Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} /> </div> </button> - <button type="button" on:click={() => dispatch('openActivityTab')}> + <button type="button" on:click={onOpenActivityTab}> <div class="flex gap-2 items-center justify-center"> <Icon path={mdiCommentOutline} class="scale-x-[-1]" size={24} /> {#if numberOfComments} diff --git a/web/src/lib/components/asset-viewer/activity-viewer.svelte b/web/src/lib/components/asset-viewer/activity-viewer.svelte index 050802e1d0..3a02454315 100644 --- a/web/src/lib/components/asset-viewer/activity-viewer.svelte +++ b/web/src/lib/components/asset-viewer/activity-viewer.svelte @@ -17,7 +17,7 @@ } from '@immich/sdk'; import { mdiClose, mdiDotsVertical, mdiHeart, mdiSend, mdiDeleteOutline } from '@mdi/js'; import * as luxon from 'luxon'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; @@ -55,6 +55,10 @@ export let albumOwnerId: string; export let disabled: boolean; export let isLiked: ActivityResponseDto | null; + export let onDeleteComment: () => void; + export let onDeleteLike: () => void; + export let onAddComment: () => void; + export let onClose: () => void; let textArea: HTMLTextAreaElement; let innerHeight: number; @@ -65,13 +69,6 @@ let message = ''; let isSendingMessage = false; - const dispatch = createEventDispatcher<{ - deleteComment: void; - deleteLike: void; - addComment: void; - close: void; - }>(); - $: { if (innerHeight && activityHeight) { divHeight = innerHeight - activityHeight; @@ -111,9 +108,9 @@ reactions.splice(index, 1); reactions = reactions; if (isLiked && reaction.type === ReactionType.Like && reaction.id == isLiked.id) { - dispatch('deleteLike'); + onDeleteLike(); } else { - dispatch('deleteComment'); + onDeleteComment(); } const deleteMessages: Record<ReactionType, string> = { @@ -141,7 +138,7 @@ reactions.push(data); textArea.style.height = '18px'; message = ''; - dispatch('addComment'); + onAddComment(); // Re-render the activity feed reactions = reactions; } catch (error) { @@ -160,7 +157,7 @@ bind:clientHeight={activityHeight} > <div class="flex place-items-center gap-2"> - <CircleIconButton on:click={() => dispatch('close')} icon={mdiClose} title={$t('close')} /> + <CircleIconButton on:click={onClose} icon={mdiClose} title={$t('close')} /> <p class="text-lg text-immich-fg dark:text-immich-dark-fg">{$t('activity')}</p> </div> diff --git a/web/src/lib/components/asset-viewer/album-list-item.svelte b/web/src/lib/components/asset-viewer/album-list-item.svelte index ab049da652..8e9f6f6b5a 100644 --- a/web/src/lib/components/asset-viewer/album-list-item.svelte +++ b/web/src/lib/components/asset-viewer/album-list-item.svelte @@ -1,16 +1,13 @@ <script lang="ts"> import { getAssetThumbnailUrl } from '$lib/utils'; import { type AlbumResponseDto } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import { normalizeSearchString } from '$lib/utils/string-utils.js'; import AlbumListItemDetails from './album-list-item-details.svelte'; - const dispatch = createEventDispatcher<{ - album: void; - }>(); - export let album: AlbumResponseDto; export let searchQuery = ''; + export let onAlbumClick: () => void; + let albumNameArray: string[] = ['', '', '']; // This part of the code is responsible for splitting album name into 3 parts where part 2 is the search query @@ -29,7 +26,7 @@ <button type="button" - on:click={() => dispatch('album')} + on:click={onAlbumClick} class="flex w-full gap-4 px-6 py-2 text-left transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl" > <span class="h-12 w-12 shrink-0 rounded-xl bg-slate-300"> diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 69d35b9aa4..850a7c159f 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -32,7 +32,7 @@ type AssetResponseDto, type StackResponseDto, } from '@immich/sdk'; - import { createEventDispatcher, onDestroy, onMount } from 'svelte'; + import { onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; import { fly } from 'svelte/transition'; import Thumbnail from '../assets/thumbnail/thumbnail.svelte'; @@ -56,8 +56,10 @@ export let isShared = false; export let album: AlbumResponseDto | null = null; export let onAction: OnAction | undefined = undefined; - - let reactions: ActivityResponseDto[] = []; + export let reactions: ActivityResponseDto[] = []; + export let onClose: (dto: { asset: AssetResponseDto }) => void; + export let onNext: () => void; + export let onPrevious: () => void; const { setAsset } = assetViewingStore; const { @@ -67,13 +69,6 @@ slideshowState, } = slideshowStore; - const dispatch = createEventDispatcher<{ - action: { type: AssetAction; asset: AssetResponseDto }; - close: { asset: AssetResponseDto }; - next: void; - previous: void; - }>(); - let appearsInAlbums: AlbumResponseDto[] = []; let shouldPlayMotionPhoto = false; let sharedLink = getSharedLink(); @@ -267,7 +262,7 @@ }; const closeViewer = () => { - dispatch('close', { asset }); + onClose({ asset }); }; const closeEditor = () => { @@ -316,7 +311,8 @@ } e?.stopPropagation(); - dispatch(order); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + order === 'previous' ? onPrevious() : onNext(); }; // const showEditorHandler = () => { @@ -533,8 +529,8 @@ disabled={!album?.isActivityEnabled} {isLiked} {numberOfComments} - on:favorite={handleFavorite} - on:openActivityTab={handleOpenActivity} + onFavorite={handleFavorite} + onOpenActivityTab={handleOpenActivity} /> </div> {/if} @@ -555,7 +551,7 @@ class="z-[1002] row-start-1 row-span-4 w-[360px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg" translate="yes" > - <DetailPanel {asset} currentAlbum={album} albums={appearsInAlbums} on:close={() => ($isShowDetail = false)} /> + <DetailPanel {asset} currentAlbum={album} albums={appearsInAlbums} onClose={() => ($isShowDetail = false)} /> </div> {/if} @@ -625,10 +621,10 @@ assetId={asset.id} {isLiked} bind:reactions - on:addComment={handleAddComment} - on:deleteComment={handleRemoveComment} - on:deleteLike={() => (isLiked = null)} - on:close={() => (isShowActivity = false)} + onAddComment={handleAddComment} + onDeleteComment={handleRemoveComment} + onDeleteLike={() => (isLiked = null)} + onClose={() => (isShowActivity = false)} /> </div> {/if} diff --git a/web/src/lib/components/asset-viewer/detail-panel-location.svelte b/web/src/lib/components/asset-viewer/detail-panel-location.svelte index a93c90d0d4..7d5d86b443 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-location.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-location.svelte @@ -81,10 +81,6 @@ {#if isShowChangeLocation} <Portal> - <ChangeLocation - {asset} - on:confirm={({ detail: gps }) => handleConfirmChangeLocation(gps)} - on:cancel={() => (isShowChangeLocation = false)} - /> + <ChangeLocation {asset} onConfirm={handleConfirmChangeLocation} onCancel={() => (isShowChangeLocation = false)} /> </Portal> {/if} diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index 340007d206..ee2da9fc3f 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -36,7 +36,6 @@ mdiPencil, } from '@mdi/js'; import { DateTime } from 'luxon'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import { slide } from 'svelte/transition'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; @@ -49,6 +48,7 @@ export let asset: AssetResponseDto; export let albums: AlbumResponseDto[] = []; export let currentAlbum: AlbumResponseDto | null = null; + export let onClose: () => void; const getDimensions = (exifInfo: ExifResponseDto) => { const { exifImageWidth: width, exifImageHeight: height } = exifInfo; @@ -106,10 +106,6 @@ ? fromDateTimeOriginal(asset.exifInfo.dateTimeOriginal, timeZone) : fromLocalDateTime(asset.localDateTime); - const dispatch = createEventDispatcher<{ - close: void; - }>(); - const getMegapixel = (width: number, height: number): number | undefined => { const megapixel = Math.round((height * width) / 1_000_000); @@ -144,7 +140,7 @@ <section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg"> <div class="flex place-items-center gap-2"> - <CircleIconButton icon={mdiClose} title={$t('close')} on:click={() => dispatch('close')} /> + <CircleIconButton icon={mdiClose} title={$t('close')} on:click={onClose} /> <p class="text-lg text-immich-fg dark:text-immich-dark-fg">{$t('info')}</p> </div> @@ -332,8 +328,8 @@ <ChangeDate initialDate={dateTime} initialTimeZone={timeZone ?? ''} - on:confirm={({ detail: date }) => handleConfirmChangeDate(date)} - on:cancel={() => (isShowChangeDate = false)} + onConfirm={handleConfirmChangeDate} + onCancel={() => (isShowChangeDate = false)} /> {/if} @@ -511,9 +507,7 @@ <PersonSidePanel assetId={asset.id} assetType={asset.type} - on:close={() => { - showEditFaces = false; - }} - on:refresh={handleRefreshPeople} + onClose={() => (showEditFaces = false)} + onRefresh={handleRefreshPeople} /> {/if} diff --git a/web/src/lib/components/asset-viewer/slideshow-bar.svelte b/web/src/lib/components/asset-viewer/slideshow-bar.svelte index 63e501f6dd..1acc06f21b 100644 --- a/web/src/lib/components/asset-viewer/slideshow-bar.svelte +++ b/web/src/lib/components/asset-viewer/slideshow-bar.svelte @@ -139,5 +139,5 @@ duration={$slideshowDelay} bind:this={progressBar} bind:status={progressBarStatus} - on:done={handleDone} + onDone={handleDone} /> diff --git a/web/src/lib/components/asset-viewer/video-native-viewer.svelte b/web/src/lib/components/asset-viewer/video-native-viewer.svelte index e0adcdfc9d..d44cb94bfb 100644 --- a/web/src/lib/components/asset-viewer/video-native-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-native-viewer.svelte @@ -4,7 +4,7 @@ import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { AssetMediaSize } from '@immich/sdk'; - import { createEventDispatcher, tick } from 'svelte'; + import { tick } from 'svelte'; import { swipe } from 'svelte-gestures'; import type { SwipeCustomEvent } from 'svelte-gestures'; import { fade } from 'svelte/transition'; @@ -13,8 +13,10 @@ export let assetId: string; export let loopVideo: boolean; export let checksum: string; - export let onPreviousAsset: () => void; - export let onNextAsset: () => void; + export let onPreviousAsset: () => void = () => {}; + export let onNextAsset: () => void = () => {}; + export let onVideoEnded: () => void = () => {}; + export let onVideoStarted: () => void = () => {}; let element: HTMLVideoElement | undefined = undefined; let isVideoLoading = true; @@ -27,12 +29,10 @@ element.load(); } - const dispatch = createEventDispatcher<{ onVideoEnded: void; onVideoStarted: void }>(); - const handleCanPlay = async (video: HTMLVideoElement) => { try { await video.play(); - dispatch('onVideoStarted'); + onVideoStarted(); } catch (error) { if (error instanceof DOMException && error.name === 'NotAllowedError' && !forceMuted) { await tryForceMutedPlay(video); @@ -75,7 +75,7 @@ use:swipe on:swipe={onSwipe} on:canplay={(e) => handleCanPlay(e.currentTarget)} - on:ended={() => dispatch('onVideoEnded')} + on:ended={onVideoEnded} on:volumechange={(e) => { if (!forceMuted) { $videoViewerMuted = e.currentTarget.muted; diff --git a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte index 5f03784c42..ae9fda8c69 100644 --- a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte @@ -15,13 +15,5 @@ {#if projectionType === ProjectionType.EQUIRECTANGULAR} <PanoramaViewer asset={{ id: assetId, type: AssetTypeEnum.Video }} /> {:else} - <VideoNativeViewer - {loopVideo} - {checksum} - {assetId} - {onPreviousAsset} - {onNextAsset} - on:onVideoEnded - on:onVideoStarted - /> + <VideoNativeViewer {loopVideo} {checksum} {assetId} {onPreviousAsset} {onNextAsset} /> {/if} diff --git a/web/src/lib/components/elements/dropdown.svelte b/web/src/lib/components/elements/dropdown.svelte index d98fb517af..0016bbe538 100644 --- a/web/src/lib/components/elements/dropdown.svelte +++ b/web/src/lib/components/elements/dropdown.svelte @@ -19,22 +19,18 @@ import LinkButton from './buttons/link-button.svelte'; import { clickOutside } from '$lib/actions/click-outside'; import { fly } from 'svelte/transition'; - import { createEventDispatcher } from 'svelte'; let className = ''; export { className as class }; - const dispatch = createEventDispatcher<{ - select: T; - 'click-outside': void; - }>(); - export let options: T[]; export let selectedOption = options[0]; export let showMenu = false; export let controlable = false; export let hideTextOnSmallScreen = true; export let title: string | undefined = undefined; + export let onSelect: (option: T) => void; + export let onClickOutside: () => void = () => {}; export let render: (item: T) => string | RenderedOption = String; @@ -43,11 +39,11 @@ showMenu = false; } - dispatch('click-outside'); + onClickOutside(); }; const handleSelectOption = (option: T) => { - dispatch('select', option); + onSelect(option); selectedOption = option; showMenu = false; diff --git a/web/src/lib/components/elements/search-bar.svelte b/web/src/lib/components/elements/search-bar.svelte index 686e5691ed..7668152d35 100644 --- a/web/src/lib/components/elements/search-bar.svelte +++ b/web/src/lib/components/elements/search-bar.svelte @@ -1,6 +1,5 @@ <script lang="ts"> import { mdiClose, mdiMagnify } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import type { SearchOptions } from '$lib/utils/dipatch'; import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; @@ -10,20 +9,20 @@ export let roundedBottom = true; export let showLoadingSpinner: boolean; export let placeholder: string; + export let onSearch: (options: SearchOptions) => void = () => {}; + export let onReset: () => void = () => {}; let inputRef: HTMLElement; - const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>(); - const resetSearch = () => { name = ''; - dispatch('reset'); + onReset(); inputRef?.focus(); }; const handleSearch = (event: KeyboardEvent) => { if (event.key === 'Enter') { - dispatch('search', { force: true }); + onSearch({ force: true }); } }; </script> @@ -38,7 +37,7 @@ title={$t('search')} size="16" padding="2" - on:click={() => dispatch('search', { force: true })} + on:click={() => onSearch({ force: true })} /> <input class="w-full gap-2 bg-gray-200 dark:bg-immich-dark-gray dark:text-white" @@ -47,7 +46,7 @@ bind:value={name} bind:this={inputRef} on:keydown={handleSearch} - on:input={() => dispatch('search', { force: false })} + on:input={() => onSearch({ force: false })} /> {#if showLoadingSpinner} <div class="flex place-items-center"> diff --git a/web/src/lib/components/faces-page/assign-face-side-panel.svelte b/web/src/lib/components/faces-page/assign-face-side-panel.svelte index eba26e6e61..ce184321e3 100644 --- a/web/src/lib/components/faces-page/assign-face-side-panel.svelte +++ b/web/src/lib/components/faces-page/assign-face-side-panel.svelte @@ -4,7 +4,6 @@ import { getPeopleThumbnailUrl } from '$lib/utils'; import { AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto } from '@immich/sdk'; import { mdiArrowLeftThin, mdiClose, mdiMagnify, mdiPlus } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import { linear } from 'svelte/easing'; import { fly } from 'svelte/transition'; import { photoViewer } from '$lib/stores/assets.store'; @@ -19,6 +18,9 @@ export let editedFace: AssetFaceResponseDto; export let assetId: string; export let assetType: AssetTypeEnum; + export let onClose: () => void; + export let onCreatePerson: (featurePhoto: string | null) => void; + export let onReassign: (person: PersonResponseDto) => void; // loading spinners let isShowLoadingNewPerson = false; @@ -31,25 +33,16 @@ $: showPeople = searchName ? searchedPeople : allPeople.filter((person) => !person.isHidden); - const dispatch = createEventDispatcher<{ - close: void; - createPerson: string | null; - reassign: PersonResponseDto; - }>(); - const handleBackButton = () => { - dispatch('close'); - }; - const handleCreatePerson = async () => { const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner); const newFeaturePhoto = await zoomImageToBase64(editedFace, assetId, assetType, $photoViewer); - dispatch('createPerson', newFeaturePhoto); + onCreatePerson(newFeaturePhoto); clearTimeout(timeout); isShowLoadingNewPerson = false; - dispatch('createPerson', newFeaturePhoto); + onCreatePerson(newFeaturePhoto); }; </script> @@ -60,7 +53,7 @@ <div class="flex place-items-center justify-between gap-2"> {#if !searchFaces} <div class="flex items-center gap-2"> - <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} /> + <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} /> <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">{$t('select_face')}</p> </div> <div class="flex justify-end gap-2"> @@ -80,7 +73,7 @@ {/if} </div> {:else} - <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} /> + <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} /> <div class="w-full flex"> <SearchPeople type="input" @@ -103,7 +96,7 @@ {#each showPeople as person (person.id)} {#if !editedFace.person || person.id !== editedFace.person.id} <div class="w-fit"> - <button type="button" class="w-[90px]" on:click={() => dispatch('reassign', person)}> + <button type="button" class="w-[90px]" on:click={() => onReassign(person)}> <div class="relative"> <ImageThumbnail curve diff --git a/web/src/lib/components/faces-page/edit-name-input.svelte b/web/src/lib/components/faces-page/edit-name-input.svelte index f48a3de15a..d9e961c13f 100644 --- a/web/src/lib/components/faces-page/edit-name-input.svelte +++ b/web/src/lib/components/faces-page/edit-name-input.svelte @@ -1,6 +1,5 @@ <script lang="ts"> import { type PersonResponseDto } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; import Button from '../elements/buttons/button.svelte'; import SearchPeople from '$lib/components/faces-page/people-search.svelte'; @@ -11,10 +10,7 @@ export let suggestedPeople: PersonResponseDto[]; export let thumbnailData: string; export let isSearchingPeople: boolean; - - const dispatch = createEventDispatcher<{ - change: string; - }>(); + export let onChange: (name: string) => void; </script> <div @@ -26,7 +22,7 @@ <form class="ml-4 flex w-full justify-between gap-16" autocomplete="off" - on:submit|preventDefault={() => dispatch('change', name)} + on:submit|preventDefault={() => onChange(name)} > <SearchPeople bind:searchName={name} diff --git a/web/src/lib/components/faces-page/face-thumbnail.svelte b/web/src/lib/components/faces-page/face-thumbnail.svelte index 58e1e0e39b..f30029ac8f 100644 --- a/web/src/lib/components/faces-page/face-thumbnail.svelte +++ b/web/src/lib/components/faces-page/face-thumbnail.svelte @@ -1,7 +1,6 @@ <script lang="ts"> import { getPeopleThumbnailUrl } from '$lib/utils'; import { type PersonResponseDto } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; export let person: PersonResponseDto; @@ -10,20 +9,13 @@ export let thumbnailSize: number | null = null; export let circle = false; export let border = false; - - let dispatch = createEventDispatcher<{ - click: PersonResponseDto; - }>(); - - const handleOnClicked = () => { - dispatch('click', person); - }; + export let onClick: (person: PersonResponseDto) => void = () => {}; </script> <button type="button" class="relative rounded-lg transition-all" - on:click={handleOnClicked} + on:click={() => onClick(person)} disabled={!selectable} style:width={thumbnailSize ? thumbnailSize + 'px' : '100%'} style:height={thumbnailSize ? thumbnailSize + 'px' : '100%'} diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte index 75f3420424..9cb3078ec0 100644 --- a/web/src/lib/components/faces-page/merge-face-selector.svelte +++ b/web/src/lib/components/faces-page/merge-face-selector.svelte @@ -6,7 +6,7 @@ import { handleError } from '$lib/utils/handle-error'; import { getAllPeople, getPerson, mergePerson, type PersonResponseDto } from '@immich/sdk'; import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { flip } from 'svelte/animate'; import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; @@ -20,15 +20,13 @@ import { t } from 'svelte-i18n'; export let person: PersonResponseDto; + export let onBack: () => void; + export let onMerge: (mergedPerson: PersonResponseDto) => void; + let people: PersonResponseDto[] = []; let selectedPeople: PersonResponseDto[] = []; let screenHeight: number; - let dispatch = createEventDispatcher<{ - back: void; - merge: PersonResponseDto; - }>(); - $: hasSelection = selectedPeople.length > 0; $: peopleToNotShow = [...selectedPeople, person]; @@ -37,10 +35,6 @@ people = data.people; }); - const onClose = () => { - dispatch('back'); - }; - const handleSwapPeople = async () => { [person, selectedPeople[0]] = [selectedPeople[0], person]; $page.url.searchParams.set(QueryParameter.ACTION, ActionQueryParameterValue.MERGE); @@ -88,7 +82,7 @@ message: $t('merged_people_count', { values: { count } }), type: NotificationType.Info, }); - dispatch('merge', mergedPerson); + onMerge(mergedPerson); } catch (error) { handleError(error, $t('cannot_merge_people')); } @@ -101,7 +95,7 @@ transition:fly={{ y: 500, duration: 100, easing: quintOut }} class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" > - <ControlAppBar on:close={onClose}> + <ControlAppBar onClose={onBack}> <svelte:fragment slot="leading"> {#if hasSelection} {$t('selected_count', { values: { count: selectedPeople.length } })} @@ -125,7 +119,7 @@ <div class="grid grid-flow-col-dense place-content-center place-items-center gap-4"> {#each selectedPeople as person (person.id)} <div animate:flip={{ duration: 250, easing: quintOut }}> - <FaceThumbnail border circle {person} selectable thumbnailSize={120} on:click={() => onSelect(person)} /> + <FaceThumbnail border circle {person} selectable thumbnailSize={120} onClick={() => onSelect(person)} /> </div> {/each} @@ -152,7 +146,7 @@ </div> </div> - <PeopleList {people} {peopleToNotShow} {screenHeight} on:select={({ detail }) => onSelect(detail)} /> + <PeopleList {people} {peopleToNotShow} {screenHeight} {onSelect} /> </section> </section> </section> diff --git a/web/src/lib/components/faces-page/merge-suggestion-modal.svelte b/web/src/lib/components/faces-page/merge-suggestion-modal.svelte index d781e1cc56..f869790eba 100644 --- a/web/src/lib/components/faces-page/merge-suggestion-modal.svelte +++ b/web/src/lib/components/faces-page/merge-suggestion-modal.svelte @@ -4,7 +4,6 @@ import { getPeopleThumbnailUrl } from '$lib/utils'; import { type PersonResponseDto } from '@immich/sdk'; import { mdiArrowLeft, mdiMerge } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; import Button from '../elements/buttons/button.svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; @@ -13,25 +12,22 @@ export let personMerge1: PersonResponseDto; export let personMerge2: PersonResponseDto; export let potentialMergePeople: PersonResponseDto[]; + export let onReject: () => void; + export let onConfirm: ([personMerge1, personMerge2]: [PersonResponseDto, PersonResponseDto]) => void; + export let onClose: () => void; let choosePersonToMerge = false; const title = personMerge2.name; - const dispatch = createEventDispatcher<{ - reject: void; - confirm: [PersonResponseDto, PersonResponseDto]; - close: void; - }>(); - - const changePersonToMerge = (newperson: PersonResponseDto) => { - const index = potentialMergePeople.indexOf(newperson); + const changePersonToMerge = (newPerson: PersonResponseDto) => { + const index = potentialMergePeople.indexOf(newPerson); [potentialMergePeople[index], personMerge2] = [personMerge2, potentialMergePeople[index]]; choosePersonToMerge = false; }; </script> -<FullScreenModal title="{$t('merge_people')} - {title}" onClose={() => dispatch('close')}> +<FullScreenModal title="{$t('merge_people')} - {title}" {onClose}> <div class="flex items-center justify-center py-4 md:h-36 md:py-4"> {#if !choosePersonToMerge} <div class="flex h-20 w-20 items-center px-1 md:h-24 md:w-24 md:px-2"> @@ -105,7 +101,7 @@ <p class="text-sm text-gray-500 dark:text-gray-300">{$t('they_will_be_merged_together')}</p> </div> <svelte:fragment slot="sticky-bottom"> - <Button fullwidth color="gray" on:click={() => dispatch('reject')}>{$t('no')}</Button> - <Button fullwidth on:click={() => dispatch('confirm', [personMerge1, personMerge2])}>{$t('yes')}</Button> + <Button fullwidth color="gray" on:click={onReject}>{$t('no')}</Button> + <Button fullwidth on:click={() => onConfirm([personMerge1, personMerge2])}>{$t('yes')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/faces-page/people-card.svelte b/web/src/lib/components/faces-page/people-card.svelte index 21f48e42eb..6791a26232 100644 --- a/web/src/lib/components/faces-page/people-card.svelte +++ b/web/src/lib/components/faces-page/people-card.svelte @@ -9,7 +9,6 @@ mdiDotsVertical, mdiEyeOffOutline, } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; import MenuOption from '../shared-components/context-menu/menu-option.svelte'; import { t } from 'svelte-i18n'; @@ -18,19 +17,12 @@ export let person: PersonResponseDto; export let preload = false; - - type MenuItemEvent = 'change-name' | 'set-birth-date' | 'merge-people' | 'hide-person'; - let dispatch = createEventDispatcher<{ - 'change-name': void; - 'set-birth-date': void; - 'merge-people': void; - 'hide-person': void; - }>(); + export let onChangeName: () => void; + export let onSetBirthDate: () => void; + export let onMergePeople: () => void; + export let onHidePerson: () => void; let showVerticalDots = false; - const onMenuClick = (event: MenuItemEvent) => { - dispatch(event); - }; </script> <div @@ -76,18 +68,10 @@ icon={mdiDotsVertical} title={$t('show_person_options')} > - <MenuOption onClick={() => onMenuClick('hide-person')} icon={mdiEyeOffOutline} text={$t('hide_person')} /> - <MenuOption onClick={() => onMenuClick('change-name')} icon={mdiAccountEditOutline} text={$t('change_name')} /> - <MenuOption - onClick={() => onMenuClick('set-birth-date')} - icon={mdiCalendarEditOutline} - text={$t('set_date_of_birth')} - /> - <MenuOption - onClick={() => onMenuClick('merge-people')} - icon={mdiAccountMultipleCheckOutline} - text={$t('merge_people')} - /> + <MenuOption onClick={onHidePerson} icon={mdiEyeOffOutline} text={$t('hide_person')} /> + <MenuOption onClick={onChangeName} icon={mdiAccountEditOutline} text={$t('change_name')} /> + <MenuOption onClick={onSetBirthDate} icon={mdiCalendarEditOutline} text={$t('set_date_of_birth')} /> + <MenuOption onClick={onMergePeople} icon={mdiAccountMultipleCheckOutline} text={$t('merge_people')} /> </ButtonContextMenu> </div> {/if} diff --git a/web/src/lib/components/faces-page/people-list.svelte b/web/src/lib/components/faces-page/people-list.svelte index 5130baf30b..230c8750ae 100644 --- a/web/src/lib/components/faces-page/people-list.svelte +++ b/web/src/lib/components/faces-page/people-list.svelte @@ -1,6 +1,5 @@ <script lang="ts"> import { type PersonResponseDto } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import FaceThumbnail from './face-thumbnail.svelte'; import SearchPeople from '$lib/components/faces-page/people-search.svelte'; import { t } from 'svelte-i18n'; @@ -8,15 +7,13 @@ export let screenHeight: number; export let people: PersonResponseDto[]; export let peopleToNotShow: PersonResponseDto[]; + export let onSelect: (person: PersonResponseDto) => void; + let searchedPeopleLocal: PersonResponseDto[] = []; let name = ''; let showPeople: PersonResponseDto[]; - let dispatch = createEventDispatcher<{ - select: PersonResponseDto; - }>(); - $: { showPeople = name ? searchedPeopleLocal : people; showPeople = showPeople.filter( @@ -35,15 +32,7 @@ > <div class="grid-col-2 grid gap-8 md:grid-cols-3 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10"> {#each showPeople as person (person.id)} - <FaceThumbnail - {person} - on:click={() => { - dispatch('select', person); - }} - circle - border - selectable - /> + <FaceThumbnail {person} on:click={() => onSelect(person)} circle border selectable /> {/each} </div> </div> diff --git a/web/src/lib/components/faces-page/person-side-panel.svelte b/web/src/lib/components/faces-page/person-side-panel.svelte index fd4fbdf964..23682f65f4 100644 --- a/web/src/lib/components/faces-page/person-side-panel.svelte +++ b/web/src/lib/components/faces-page/person-side-panel.svelte @@ -18,7 +18,7 @@ import { mdiAccountOff } from '@mdi/js'; import Icon from '$lib/components/elements/icon.svelte'; import { mdiArrowLeftThin, mdiMinus, mdiRestart } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { linear } from 'svelte/easing'; import { fly } from 'svelte/transition'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; @@ -31,6 +31,8 @@ export let assetId: string; export let assetType: AssetTypeEnum; + export let onClose: () => void; + export let onRefresh: () => void; // keep track of the changes let peopleToCreate: string[] = []; @@ -56,11 +58,6 @@ const thumbnailWidth = '90px'; - const dispatch = createEventDispatcher<{ - close: void; - refresh: void; - }>(); - async function loadPeople() { const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner); try { @@ -85,7 +82,7 @@ ) { clearTimeout(loaderLoadingDoneTimeout); clearTimeout(automaticRefreshTimeout); - dispatch('refresh'); + onRefresh(); } }; @@ -98,10 +95,6 @@ return b.every((valueB) => a.includes(valueB)); }; - const handleBackButton = () => { - dispatch('close'); - }; - const handleReset = (id: string) => { if (selectedPersonToReassign[id]) { delete selectedPersonToReassign[id]; @@ -153,9 +146,9 @@ isShowLoadingDone = false; if (peopleToCreate.length === 0) { clearTimeout(loaderLoadingDoneTimeout); - dispatch('refresh'); + onRefresh(); } else { - automaticRefreshTimeout = setTimeout(() => dispatch('refresh'), 15_000); + automaticRefreshTimeout = setTimeout(onRefresh, 15_000); } }; @@ -185,7 +178,7 @@ > <div class="flex place-items-center justify-between gap-2"> <div class="flex items-center gap-2"> - <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} /> + <CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} /> <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">{$t('edit_faces')}</p> </div> {#if !isShowLoadingDone} @@ -336,8 +329,8 @@ {editedFace} {assetId} {assetType} - on:close={() => (showSelectedFaces = false)} - on:createPerson={(event) => handleCreatePerson(event.detail)} - on:reassign={(event) => handleReassignFace(event.detail)} + onClose={() => (showSelectedFaces = false)} + onCreatePerson={handleCreatePerson} + onReassign={handleReassignFace} /> {/if} diff --git a/web/src/lib/components/faces-page/set-birth-date-modal.svelte b/web/src/lib/components/faces-page/set-birth-date-modal.svelte index b670f34dfd..d38c519911 100644 --- a/web/src/lib/components/faces-page/set-birth-date-modal.svelte +++ b/web/src/lib/components/faces-page/set-birth-date-modal.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { mdiCake } from '@mdi/js'; @@ -7,28 +6,20 @@ import { t } from 'svelte-i18n'; export let birthDate: string; - - const dispatch = createEventDispatcher<{ - close: void; - updated: string; - }>(); + export let onClose: () => void; + export let onUpdate: (birthDate: string) => void; const todayFormatted = new Date().toISOString().split('T')[0]; - - const handleCancel = () => dispatch('close'); - const handleSubmit = () => { - dispatch('updated', birthDate); - }; </script> -<FullScreenModal title={$t('set_date_of_birth')} icon={mdiCake} onClose={handleCancel}> +<FullScreenModal title={$t('set_date_of_birth')} icon={mdiCake} {onClose}> <div class="text-immich-primary dark:text-immich-dark-primary"> <p class="text-sm dark:text-immich-dark-fg"> {$t('birthdate_set_description')} </p> </div> - <form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="set-birth-date-form"> + <form on:submit|preventDefault={() => onUpdate(birthDate)} autocomplete="off" id="set-birth-date-form"> <div class="my-4 flex flex-col gap-2"> <DateInput class="immich-form-input" @@ -41,7 +32,7 @@ </div> </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" fullwidth on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button color="gray" fullwidth on:click={onClose}>{$t('cancel')}</Button> <Button type="submit" fullwidth form="set-birth-date-form">{$t('set')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/faces-page/unmerge-face-selector.svelte b/web/src/lib/components/faces-page/unmerge-face-selector.svelte index c89c8338d3..753e46c219 100644 --- a/web/src/lib/components/faces-page/unmerge-face-selector.svelte +++ b/web/src/lib/components/faces-page/unmerge-face-selector.svelte @@ -10,7 +10,7 @@ type PersonResponseDto, } from '@immich/sdk'; import { mdiMerge, mdiPlus } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; import Button from '../elements/buttons/button.svelte'; @@ -23,6 +23,8 @@ export let assetIds: string[]; export let personAssets: PersonResponseDto; + export let onConfirm: () => void; + export let onClose: () => void; let people: PersonResponseDto[] = []; let selectedPerson: PersonResponseDto | null = null; @@ -34,11 +36,6 @@ $: peopleToNotShow = selectedPerson ? [personAssets, selectedPerson] : [personAssets]; - let dispatch = createEventDispatcher<{ - confirm: void; - close: void; - }>(); - const selectedPeople: AssetFaceUpdateItem[] = []; for (const assetId of assetIds) { @@ -50,10 +47,6 @@ people = data.people; }); - const onClose = () => { - dispatch('close'); - }; - const handleSelectedPerson = (person: PersonResponseDto) => { if (selectedPerson && selectedPerson.id === person.id) { handleRemoveSelectedPerson(); @@ -87,7 +80,7 @@ } showLoadingSpinnerCreate = false; - dispatch('confirm'); + onConfirm(); }; const handleReassign = async () => { @@ -113,7 +106,7 @@ } showLoadingSpinnerReassign = false; - dispatch('confirm'); + onConfirm(); }; </script> @@ -123,7 +116,7 @@ transition:fly={{ y: 500, duration: 100, easing: quintOut }} class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg" > - <ControlAppBar on:close={onClose}> + <ControlAppBar {onClose}> <svelte:fragment slot="leading"> <slot name="header" /> <div /> @@ -180,7 +173,7 @@ </div> </div> {/if} - <PeopleList {people} {peopleToNotShow} {screenHeight} on:select={({ detail }) => handleSelectedPerson(detail)} /> + <PeopleList {people} {peopleToNotShow} {screenHeight} onSelect={handleSelectedPerson} /> </section> </section> </section> diff --git a/web/src/lib/components/forms/api-key-secret.svelte b/web/src/lib/components/forms/api-key-secret.svelte index b7bf8e1836..f43e1da38e 100644 --- a/web/src/lib/components/forms/api-key-secret.svelte +++ b/web/src/lib/components/forms/api-key-secret.svelte @@ -1,20 +1,15 @@ <script lang="ts"> import { copyToClipboard } from '$lib/utils'; import { mdiKeyVariant } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { t } from 'svelte-i18n'; export let secret = ''; - - const dispatch = createEventDispatcher<{ - done: void; - }>(); - const handleDone = () => dispatch('done'); + export let onDone: () => void; </script> -<FullScreenModal title={$t('api_key')} icon={mdiKeyVariant} onClose={() => handleDone()}> +<FullScreenModal title={$t('api_key')} icon={mdiKeyVariant} onClose={onDone}> <div class="text-immich-primary dark:text-immich-dark-primary"> <p class="text-sm dark:text-immich-dark-fg"> {$t('api_key_description')} @@ -28,6 +23,6 @@ <svelte:fragment slot="sticky-bottom"> <Button on:click={() => copyToClipboard(secret)} fullwidth>{$t('copy_to_clipboard')}</Button> - <Button on:click={() => handleDone()} fullwidth>{$t('done')}</Button> + <Button on:click={onDone} fullwidth>{$t('done')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/forms/change-password-form.svelte b/web/src/lib/components/forms/change-password-form.svelte index 799dde7ef3..cbf2ff07f0 100644 --- a/web/src/lib/components/forms/change-password-form.svelte +++ b/web/src/lib/components/forms/change-password-form.svelte @@ -1,10 +1,11 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import PasswordField from '../shared-components/password-field.svelte'; import { updateMyUser } from '@immich/sdk'; import { t } from 'svelte-i18n'; + export let onSuccess: () => void; + let errorMessage: string; let success: string; @@ -23,17 +24,13 @@ } } - const dispatch = createEventDispatcher<{ - success: void; - }>(); - async function changePassword() { if (valid) { errorMessage = ''; await updateMyUser({ userUpdateMeDto: { password: String(password) } }); - dispatch('success'); + onSuccess(); } } </script> diff --git a/web/src/lib/components/forms/create-user-form.svelte b/web/src/lib/components/forms/create-user-form.svelte index 8f049685a4..9c4b83002b 100644 --- a/web/src/lib/components/forms/create-user-form.svelte +++ b/web/src/lib/components/forms/create-user-form.svelte @@ -5,13 +5,14 @@ import { ByteUnit, convertToBytes } from '$lib/utils/byte-units'; import { handleError } from '$lib/utils/handle-error'; import { createUserAdmin } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import Button from '../elements/buttons/button.svelte'; import Slider from '../elements/slider.svelte'; import PasswordField from '../shared-components/password-field.svelte'; export let onClose: () => void; + export let onSubmit: () => void; + export let onCancel: () => void; let error: string; let success: string; @@ -39,10 +40,6 @@ canCreateUser = true; } } - const dispatch = createEventDispatcher<{ - submit: void; - cancel: void; - }>(); async function registerUser() { if (canCreateUser && !isCreatingUser) { @@ -63,7 +60,7 @@ success = $t('new_user_created'); - dispatch('submit'); + onSubmit(); return; } catch (error) { @@ -132,7 +129,7 @@ {/if} </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" fullwidth on:click={() => dispatch('cancel')}>{$t('cancel')}</Button> + <Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button> <Button type="submit" disabled={isCreatingUser} fullwidth form="create-new-user-form">{$t('create')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/forms/edit-user-form.svelte b/web/src/lib/components/forms/edit-user-form.svelte index b326565122..0079a695bc 100644 --- a/web/src/lib/components/forms/edit-user-form.svelte +++ b/web/src/lib/components/forms/edit-user-form.svelte @@ -5,7 +5,6 @@ import { handleError } from '$lib/utils/handle-error'; import { updateUserAdmin, type UserAdminResponseDto } from '@immich/sdk'; import { mdiAccountEditOutline } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import { dialogController } from '$lib/components/shared-components/dialog/dialog'; import { t } from 'svelte-i18n'; @@ -15,6 +14,8 @@ export let canResetPassword = true; export let newPassword: string; export let onClose: () => void; + export let onResetPasswordSuccess: () => void; + export let onEditSuccess: () => void; let error: string; let success: string; @@ -27,12 +28,6 @@ !!quotaSize && convertToBytes(Number(quotaSize), ByteUnit.GiB) > $serverInfo.diskSizeRaw; - const dispatch = createEventDispatcher<{ - close: void; - resetPasswordSuccess: void; - editSuccess: void; - }>(); - const editUser = async () => { try { const { id, email, name, storageLabel } = user; @@ -46,7 +41,7 @@ }, }); - dispatch('editSuccess'); + onEditSuccess(); } catch (error) { handleError(error, $t('errors.unable_to_update_user')); } @@ -72,7 +67,7 @@ }, }); - dispatch('resetPasswordSuccess'); + onResetPasswordSuccess(); } catch (error) { handleError(error, $t('errors.unable_to_reset_password')); } diff --git a/web/src/lib/components/forms/library-exclusion-pattern-form.svelte b/web/src/lib/components/forms/library-exclusion-pattern-form.svelte index c09f1fbaf6..05d47c0a0f 100644 --- a/web/src/lib/components/forms/library-exclusion-pattern-form.svelte +++ b/web/src/lib/components/forms/library-exclusion-pattern-form.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { mdiFolderRemove } from '@mdi/js'; @@ -10,6 +9,9 @@ export let exclusionPatterns: string[] = []; export let isEditing = false; export let submitText = $t('submit'); + export let onCancel: () => void; + export let onSubmit: (exclusionPattern: string) => void; + export let onDelete: () => void = () => {}; onMount(() => { if (isEditing) { @@ -19,18 +21,10 @@ $: isDuplicate = exclusionPattern !== null && exclusionPatterns.includes(exclusionPattern); $: canSubmit = exclusionPattern && !exclusionPatterns.includes(exclusionPattern); - - const dispatch = createEventDispatcher<{ - cancel: void; - submit: { excludePattern: string }; - delete: void; - }>(); - const handleCancel = () => dispatch('cancel'); - const handleSubmit = () => dispatch('submit', { excludePattern: exclusionPattern }); </script> -<FullScreenModal title={$t('add_exclusion_pattern')} icon={mdiFolderRemove} onClose={handleCancel}> - <form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="add-exclusion-pattern-form"> +<FullScreenModal title={$t('add_exclusion_pattern')} icon={mdiFolderRemove} onClose={onCancel}> + <form on:submit|preventDefault={() => onSubmit(exclusionPattern)} autocomplete="off" id="add-exclusion-pattern-form"> <p class="py-5 text-sm"> {$t('admin.exclusion_pattern_description')} <br /><br /> @@ -53,9 +47,9 @@ </div> </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" fullwidth on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button> {#if isEditing} - <Button color="red" fullwidth on:click={() => dispatch('delete')}>{$t('delete')}</Button> + <Button color="red" fullwidth on:click={onDelete}>{$t('delete')}</Button> {/if} <Button type="submit" disabled={!canSubmit} fullwidth form="add-exclusion-pattern-form">{submitText}</Button> </svelte:fragment> diff --git a/web/src/lib/components/forms/library-import-path-form.svelte b/web/src/lib/components/forms/library-import-path-form.svelte index f82d573386..8bfca80aec 100644 --- a/web/src/lib/components/forms/library-import-path-form.svelte +++ b/web/src/lib/components/forms/library-import-path-form.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { mdiFolderSync } from '@mdi/js'; @@ -12,6 +11,9 @@ export let cancelText = $t('cancel'); export let submitText = $t('save'); export let isEditing = false; + export let onCancel: () => void; + export let onSubmit: (importPath: string | null) => void; + export let onDelete: () => void = () => {}; onMount(() => { if (isEditing) { @@ -21,18 +23,10 @@ $: isDuplicate = importPath !== null && importPaths.includes(importPath); $: canSubmit = importPath !== '' && importPath !== null && !importPaths.includes(importPath); - - const dispatch = createEventDispatcher<{ - cancel: void; - submit: { importPath: string | null }; - delete: void; - }>(); - const handleCancel = () => dispatch('cancel'); - const handleSubmit = () => dispatch('submit', { importPath }); </script> -<FullScreenModal {title} icon={mdiFolderSync} onClose={handleCancel}> - <form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="library-import-path-form"> +<FullScreenModal {title} icon={mdiFolderSync} onClose={onCancel}> + <form on:submit|preventDefault={() => onSubmit(importPath)} autocomplete="off" id="library-import-path-form"> <p class="py-5 text-sm">{$t('admin.library_import_path_description')}</p> <div class="my-4 flex flex-col gap-2"> @@ -47,9 +41,9 @@ </div> </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button> + <Button color="gray" fullwidth on:click={onCancel}>{cancelText}</Button> {#if isEditing} - <Button color="red" fullwidth on:click={() => dispatch('delete')}>{$t('delete')}</Button> + <Button color="red" fullwidth on:click={onDelete}>{$t('delete')}</Button> {/if} <Button type="submit" disabled={!canSubmit} fullwidth form="library-import-path-form">{submitText}</Button> </svelte:fragment> diff --git a/web/src/lib/components/forms/library-import-paths-form.svelte b/web/src/lib/components/forms/library-import-paths-form.svelte index a2bb3a9686..9e7ae11a63 100644 --- a/web/src/lib/components/forms/library-import-paths-form.svelte +++ b/web/src/lib/components/forms/library-import-paths-form.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { handleError } from '../../utils/handle-error'; import Button from '../elements/buttons/button.svelte'; import LibraryImportPathForm from './library-import-path-form.svelte'; @@ -12,6 +12,8 @@ import { t } from 'svelte-i18n'; export let library: LibraryResponseDto; + export let onCancel: () => void; + export let onSubmit: (library: LibraryResponseDto) => void; let addImportPath = false; let editImportPath: number | null = null; @@ -65,19 +67,6 @@ } }; - const dispatch = createEventDispatcher<{ - cancel: void; - submit: Partial<LibraryResponseDto>; - }>(); - - const handleCancel = () => { - dispatch('cancel'); - }; - - const handleSubmit = () => { - dispatch('submit', { ...library }); - }; - const handleAddImportPath = async () => { if (!addImportPath || !importPathToAdd) { return; @@ -153,8 +142,8 @@ submitText={$t('add')} bind:importPath={importPathToAdd} {importPaths} - on:submit={handleAddImportPath} - on:cancel={() => { + onSubmit={handleAddImportPath} + onCancel={() => { addImportPath = false; importPathToAdd = null; }} @@ -168,15 +157,13 @@ isEditing={true} bind:importPath={editedImportPath} {importPaths} - on:submit={handleEditImportPath} - on:delete={handleDeleteImportPath} - on:cancel={() => { - editImportPath = null; - }} + onSubmit={handleEditImportPath} + onDelete={handleDeleteImportPath} + onCancel={() => (editImportPath = null)} /> {/if} -<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" class="m-4 flex flex-col gap-4"> +<form on:submit|preventDefault={() => onSubmit({ ...library })} autocomplete="off" class="m-4 flex flex-col gap-4"> <table class="text-left"> <tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray"> {#each validatedPaths as validatedPath, listIndex} @@ -251,7 +238,7 @@ > </div> <div class="justify-end gap-2"> - <Button size="sm" color="gray" on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button> <Button size="sm" type="submit">{$t('save')}</Button> </div> </div> diff --git a/web/src/lib/components/forms/library-rename-form.svelte b/web/src/lib/components/forms/library-rename-form.svelte index e09e0a4f2b..1f93fb028b 100644 --- a/web/src/lib/components/forms/library-rename-form.svelte +++ b/web/src/lib/components/forms/library-rename-form.svelte @@ -1,31 +1,20 @@ <script lang="ts"> import type { LibraryResponseDto } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import { t } from 'svelte-i18n'; export let library: Partial<LibraryResponseDto>; - - const dispatch = createEventDispatcher<{ - cancel: void; - submit: Partial<LibraryResponseDto>; - }>(); - const handleCancel = () => { - dispatch('cancel'); - }; - - const handleSubmit = () => { - dispatch('submit', { ...library }); - }; + export let onCancel: () => void; + export let onSubmit: (library: Partial<LibraryResponseDto>) => void; </script> -<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" class="m-4 flex flex-col gap-2"> +<form on:submit|preventDefault={() => onSubmit({ ...library })} autocomplete="off" class="m-4 flex flex-col gap-2"> <div class="flex flex-col gap-2"> <label class="immich-form-label" for="path">{$t('name')}</label> <input class="immich-form-input" id="name" name="name" type="text" bind:value={library.name} /> </div> <div class="flex w-full justify-end gap-2 pt-2"> - <Button size="sm" color="gray" on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button> <Button size="sm" type="submit">{$t('save')}</Button> </div> </form> diff --git a/web/src/lib/components/forms/library-scan-settings-form.svelte b/web/src/lib/components/forms/library-scan-settings-form.svelte index 5e025a406a..a9a42c31f7 100644 --- a/web/src/lib/components/forms/library-scan-settings-form.svelte +++ b/web/src/lib/components/forms/library-scan-settings-form.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import { type LibraryResponseDto } from '@immich/sdk'; import { mdiPencilOutline } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { handleError } from '../../utils/handle-error'; import Button from '../elements/buttons/button.svelte'; import LibraryExclusionPatternForm from './library-exclusion-pattern-form.svelte'; @@ -9,6 +9,8 @@ import { t } from 'svelte-i18n'; export let library: Partial<LibraryResponseDto>; + export let onCancel: () => void; + export let onSubmit: (library: Partial<LibraryResponseDto>) => void; let addExclusionPattern = false; let editExclusionPattern: number | null = null; @@ -26,18 +28,6 @@ } }); - const dispatch = createEventDispatcher<{ - cancel: void; - submit: Partial<LibraryResponseDto>; - }>(); - const handleCancel = () => { - dispatch('cancel'); - }; - - const handleSubmit = () => { - dispatch('submit', library); - }; - const handleAddExclusionPattern = () => { if (!addExclusionPattern) { return; @@ -106,10 +96,8 @@ submitText={$t('add')} bind:exclusionPattern={exclusionPatternToAdd} {exclusionPatterns} - on:submit={handleAddExclusionPattern} - on:cancel={() => { - addExclusionPattern = false; - }} + onSubmit={handleAddExclusionPattern} + onCancel={() => (addExclusionPattern = false)} /> {/if} @@ -119,15 +107,13 @@ isEditing={true} bind:exclusionPattern={editedExclusionPattern} {exclusionPatterns} - on:submit={handleEditExclusionPattern} - on:delete={handleDeleteExclusionPattern} - on:cancel={() => { - editExclusionPattern = null; - }} + onSubmit={handleEditExclusionPattern} + onDelete={handleDeleteExclusionPattern} + onCancel={() => (editExclusionPattern = null)} /> {/if} -<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" class="m-4 flex flex-col gap-4"> +<form on:submit|preventDefault={() => onSubmit(library)} autocomplete="off" class="m-4 flex flex-col gap-4"> <table class="w-full text-left"> <tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray"> {#each exclusionPatterns as exclusionPattern, listIndex} @@ -178,7 +164,7 @@ </table> <div class="flex w-full justify-end gap-4"> - <Button size="sm" color="gray" on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button size="sm" color="gray" on:click={onCancel}>{$t('cancel')}</Button> <Button size="sm" type="submit">{$t('save')}</Button> </div> </form> diff --git a/web/src/lib/components/forms/library-user-picker-form.svelte b/web/src/lib/components/forms/library-user-picker-form.svelte index 4b4e7308dc..e5334ff9e9 100644 --- a/web/src/lib/components/forms/library-user-picker-form.svelte +++ b/web/src/lib/components/forms/library-user-picker-form.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { mdiFolderSync } from '@mdi/js'; @@ -9,6 +8,9 @@ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import { t } from 'svelte-i18n'; + export let onCancel: () => void; + export let onSubmit: (ownerId: string) => void; + let ownerId: string = $user.id; let userOptions: { value: string; text: string }[] = []; @@ -17,25 +19,16 @@ const users = await searchUsersAdmin({}); userOptions = users.map((user) => ({ value: user.id, text: user.name })); }); - - const dispatch = createEventDispatcher<{ - cancel: void; - submit: { ownerId: string }; - delete: void; - }>(); - - const handleCancel = () => dispatch('cancel'); - const handleSubmit = () => dispatch('submit', { ownerId }); </script> -<FullScreenModal title={$t('select_library_owner')} icon={mdiFolderSync} onClose={handleCancel}> - <form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="select-library-owner-form"> +<FullScreenModal title={$t('select_library_owner')} icon={mdiFolderSync} onClose={onCancel}> + <form on:submit|preventDefault={() => onSubmit(ownerId)} autocomplete="off" id="select-library-owner-form"> <p class="p-5 text-sm">{$t('admin.note_cannot_be_changed_later')}</p> <SettingSelect bind:value={ownerId} options={userOptions} name="user" /> </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" fullwidth on:click={() => handleCancel()}>{$t('cancel')}</Button> + <Button color="gray" fullwidth on:click={onCancel}>{$t('cancel')}</Button> <Button type="submit" fullwidth form="select-library-owner-form">{$t('create')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index 5bca13b060..ed232b80cd 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -21,7 +21,7 @@ <header> {#if !hideNavbar} - <NavigationBar {showUploadButton} on:uploadClicked={() => openFileUploadDialog()} /> + <NavigationBar {showUploadButton} onUploadClick={() => openFileUploadDialog()} /> {/if} <slot name="header" /> diff --git a/web/src/lib/components/map-page/map-settings-modal.svelte b/web/src/lib/components/map-page/map-settings-modal.svelte index b442396c84..35df9f2285 100644 --- a/web/src/lib/components/map-page/map-settings-modal.svelte +++ b/web/src/lib/components/map-page/map-settings-modal.svelte @@ -4,7 +4,6 @@ import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import type { MapSettings } from '$lib/stores/preferences.store'; import { Duration } from 'luxon'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import { fly } from 'svelte/transition'; import Button from '../elements/buttons/button.svelte'; @@ -12,19 +11,15 @@ import DateInput from '../elements/date-input.svelte'; export let settings: MapSettings; + export let onClose: () => void; + export let onSave: (settings: MapSettings) => void; + let customDateRange = !!settings.dateAfter || !!settings.dateBefore; - - const dispatch = createEventDispatcher<{ - close: void; - save: MapSettings; - }>(); - - const handleClose = () => dispatch('close'); </script> -<FullScreenModal title={$t('map_settings')} onClose={handleClose}> +<FullScreenModal title={$t('map_settings')} {onClose}> <form - on:submit|preventDefault={() => dispatch('save', settings)} + on:submit|preventDefault={() => onSave(settings)} class="flex flex-col gap-4 text-immich-primary dark:text-immich-dark-primary" id="map-settings-form" > @@ -108,7 +103,7 @@ {/if} </form> <svelte:fragment slot="sticky-bottom"> - <Button color="gray" size="sm" fullwidth on:click={handleClose}>{$t('cancel')}</Button> + <Button color="gray" size="sm" fullwidth on:click={onClose}>{$t('cancel')}</Button> <Button type="submit" size="sm" fullwidth form="map-settings-form">{$t('save')}</Button> </svelte:fragment> </FullScreenModal> diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index ae6416873e..919433f79b 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -250,7 +250,7 @@ <section id="memory-viewer" class="w-full bg-immich-dark-gray" bind:this={memoryWrapper}> {#if current && current.memory.assets.length > 0} - <ControlAppBar on:close={() => goto(AppRoute.PHOTOS)} forceDark> + <ControlAppBar onClose={() => goto(AppRoute.PHOTOS)} forceDark> <svelte:fragment slot="leading"> <p class="text-lg"> {$memoryLaneTitle(current.memory.yearsAgo)} diff --git a/web/src/lib/components/photos-page/actions/add-to-album.svelte b/web/src/lib/components/photos-page/actions/add-to-album.svelte index 976f4bd9cf..d3998510cd 100644 --- a/web/src/lib/components/photos-page/actions/add-to-album.svelte +++ b/web/src/lib/components/photos-page/actions/add-to-album.svelte @@ -40,8 +40,8 @@ {#if showAlbumPicker} <AlbumSelectionModal {shared} - on:newAlbum={({ detail }) => handleAddToNewAlbum(detail)} - on:album={({ detail }) => handleAddToAlbum(detail)} + onNewAlbum={handleAddToNewAlbum} + onAlbumClick={handleAddToAlbum} onClose={handleHideAlbumPicker} /> {/if} diff --git a/web/src/lib/components/photos-page/actions/change-date-action.svelte b/web/src/lib/components/photos-page/actions/change-date-action.svelte index 6ee775fa69..114315348d 100644 --- a/web/src/lib/components/photos-page/actions/change-date-action.svelte +++ b/web/src/lib/components/photos-page/actions/change-date-action.svelte @@ -31,9 +31,5 @@ <MenuOption text={$t('change_date')} icon={mdiCalendarEditOutline} onClick={() => (isShowChangeDate = true)} /> {/if} {#if isShowChangeDate} - <ChangeDate - initialDate={DateTime.now()} - on:confirm={({ detail: date }) => handleConfirm(date)} - on:cancel={() => (isShowChangeDate = false)} - /> + <ChangeDate initialDate={DateTime.now()} onConfirm={handleConfirm} onCancel={() => (isShowChangeDate = false)} /> {/if} diff --git a/web/src/lib/components/photos-page/actions/change-location-action.svelte b/web/src/lib/components/photos-page/actions/change-location-action.svelte index 0e19696a42..3fe1db4327 100644 --- a/web/src/lib/components/photos-page/actions/change-location-action.svelte +++ b/web/src/lib/components/photos-page/actions/change-location-action.svelte @@ -35,8 +35,5 @@ /> {/if} {#if isShowChangeLocation} - <ChangeLocation - on:confirm={({ detail: point }) => handleConfirm(point)} - on:cancel={() => (isShowChangeLocation = false)} - /> + <ChangeLocation onConfirm={handleConfirm} onCancel={() => (isShowChangeLocation = false)} /> {/if} diff --git a/web/src/lib/components/photos-page/actions/delete-assets.svelte b/web/src/lib/components/photos-page/actions/delete-assets.svelte index 5c79e7b221..6d3275c74d 100644 --- a/web/src/lib/components/photos-page/actions/delete-assets.svelte +++ b/web/src/lib/components/photos-page/actions/delete-assets.svelte @@ -49,7 +49,7 @@ {#if isShowConfirmation} <DeleteAssetDialog size={getOwnedAssets().size} - on:confirm={handleDelete} - on:cancel={() => (isShowConfirmation = false)} + onConfirm={handleDelete} + onCancel={() => (isShowConfirmation = false)} /> {/if} diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte index 240b6c2ba2..b2780cc1a0 100644 --- a/web/src/lib/components/photos-page/asset-date-group.svelte +++ b/web/src/lib/components/photos-page/asset-date-group.svelte @@ -8,7 +8,7 @@ import { findTotalOffset, type DateGroup, type ScrollTargetListener } from '$lib/utils/timeline-util'; import type { AssetResponseDto } from '@immich/sdk'; import { mdiCheckCircle, mdiCircleOutline } from '@mdi/js'; - import { createEventDispatcher, onDestroy } from 'svelte'; + import { onDestroy } from 'svelte'; import { fly } from 'svelte/transition'; import Thumbnail from '../assets/thumbnail/thumbnail.svelte'; import { TUNABLES } from '$lib/utils/tunables'; @@ -29,6 +29,9 @@ export let onScrollTarget: ScrollTargetListener | undefined = undefined; export let onAssetInGrid: ((asset: AssetResponseDto) => void) | undefined = undefined; + export let onSelect: ({ title, assets }: { title: string; assets: AssetResponseDto[] }) => void; + export let onSelectAssets: (asset: AssetResponseDto) => void; + export let onSelectAssetCandidates: (asset: AssetResponseDto | null) => void; const componentId = generateId(); $: bucketDate = bucket.bucketDate; @@ -41,11 +44,6 @@ const TITLE_HEIGHT = 51; const { selectedGroup, selectedAssets, assetSelectionCandidates, isMultiSelectState } = assetInteractionStore; - const dispatch = createEventDispatcher<{ - select: { title: string; assets: AssetResponseDto[] }; - selectAssets: AssetResponseDto; - selectAssetCandidates: AssetResponseDto | null; - }>(); let isMouseOverGroup = false; let hoveredDateGroup = ''; @@ -65,10 +63,10 @@ } }; - const handleSelectGroup = (title: string, assets: AssetResponseDto[]) => dispatch('select', { title, assets }); + const handleSelectGroup = (title: string, assets: AssetResponseDto[]) => onSelect({ title, assets }); const assetSelectHandler = (asset: AssetResponseDto, assetsInDateGroup: AssetResponseDto[], groupTitle: string) => { - dispatch('selectAssets', asset); + onSelectAssets(asset); // Check if all assets are selected in a group to toggle the group selection's icon let selectedAssetsInGroupCount = assetsInDateGroup.filter((asset) => $selectedAssets.has(asset)).length; @@ -86,7 +84,7 @@ hoveredDateGroup = groupTitle; if ($isMultiSelectState) { - dispatch('selectAssetCandidates', asset); + onSelectAssetCandidates(asset); } }; diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 3bf0c65bc9..6de36c803e 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -28,7 +28,7 @@ import { TUNABLES } from '$lib/utils/tunables'; import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk'; import { throttle } from 'lodash-es'; - import { createEventDispatcher, onDestroy, onMount } from 'svelte'; + import { onDestroy, onMount } from 'svelte'; import Portal from '../shared-components/portal/portal.svelte'; import Scrubber from '../shared-components/scrubber/scrubber.svelte'; import ShowShortcuts from '../shared-components/show-shortcuts.svelte'; @@ -64,6 +64,8 @@ export let isShared = false; export let album: AlbumResponseDto | null = null; export let isShowDeleteConfirmation = false; + export let onSelect: (asset: AssetResponseDto) => void = () => {}; + export let onEscape: () => void = () => {}; let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget } = assetViewingStore; const { assetSelectionCandidates, assetSelectionStart, selectedGroup, selectedAssets, isMultiSelectState } = @@ -127,8 +129,6 @@ }, } = TUNABLES; - const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>(); - const isViewportOrigin = () => { return viewport.height === 0 && viewport.width === 0; }; @@ -447,7 +447,7 @@ const ids = await stackAssets(Array.from($selectedAssets)); if (ids) { $assetStore.removeAssets(ids); - dispatch('escape'); + onEscape(); } }; @@ -471,7 +471,7 @@ } const shortcuts: ShortcutOptions[] = [ - { shortcut: { key: 'Escape' }, onShortcut: () => dispatch('escape') }, + { shortcut: { key: 'Escape' }, onShortcut: onEscape }, { shortcut: { key: '?', shift: true }, onShortcut: () => (showShortcuts = !showShortcuts) }, { shortcut: { key: '/' }, onShortcut: () => goto(AppRoute.EXPLORE) }, { shortcut: { key: 'A', ctrl: true }, onShortcut: () => selectAllAssets($assetStore, assetInteractionStore) }, @@ -539,7 +539,7 @@ return !!nextAsset; }; - const handleClose = async ({ detail: { asset } }: { detail: { asset: AssetResponseDto } }) => { + const handleClose = async ({ asset }: { asset: AssetResponseDto }) => { assetViewingStore.showAssetViewer(false); showSkeleton = true; $gridScrollTarget = { at: asset.id }; @@ -554,7 +554,7 @@ case AssetAction.DELETE: { // find the next asset to show or close the viewer // eslint-disable-next-line @typescript-eslint/no-unused-expressions - (await handleNext()) || (await handlePrevious()) || (await handleClose({ detail: { asset: action.asset } })); + (await handleNext()) || (await handlePrevious()) || (await handleClose({ asset: action.asset })); // delete after find the next one assetStore.removeAssets([action.asset.id]); @@ -649,7 +649,7 @@ return; } - dispatch('select', asset); + onSelect(asset); if (singleSelect) { element.scrollTop = 0; @@ -754,8 +754,8 @@ {#if isShowDeleteConfirmation} <DeleteAssetDialog size={idsSelectedAssets.length} - on:cancel={() => (isShowDeleteConfirmation = false)} - on:confirm={() => handlePromiseError(trashOrDelete(true))} + onCancel={() => (isShowDeleteConfirmation = false)} + onConfirm={() => handlePromiseError(trashOrDelete(true))} /> {/if} @@ -847,9 +847,9 @@ {onAssetInGrid} {bucket} viewport={safeViewport} - on:select={({ detail: group }) => handleGroupSelect(group.title, group.assets)} - on:selectAssetCandidates={({ detail: asset }) => handleSelectAssetCandidates(asset)} - on:selectAssets={({ detail: asset }) => handleSelectAssets(asset)} + onSelect={({ title, assets }) => handleGroupSelect(title, assets)} + onSelectAssetCandidates={handleSelectAssetCandidates} + onSelectAssets={handleSelectAssets} /> {/if} </div> @@ -869,9 +869,9 @@ {isShared} {album} onAction={handleAction} - on:previous={handlePrevious} - on:next={handleNext} - on:close={handleClose} + onPrevious={handlePrevious} + onNext={handleNext} + onClose={handleClose} /> {/await} {/if} diff --git a/web/src/lib/components/photos-page/asset-select-control-bar.svelte b/web/src/lib/components/photos-page/asset-select-control-bar.svelte index c802c53454..79a0ea75e6 100644 --- a/web/src/lib/components/photos-page/asset-select-control-bar.svelte +++ b/web/src/lib/components/photos-page/asset-select-control-bar.svelte @@ -30,7 +30,7 @@ }); </script> -<ControlAppBar on:close={clearSelect} backIcon={mdiClose} tailwindClasses="bg-white shadow-md"> +<ControlAppBar onClose={clearSelect} backIcon={mdiClose} tailwindClasses="bg-white shadow-md"> <div class="font-medium text-immich-primary dark:text-immich-dark-primary" slot="leading"> <p class="block sm:hidden">{assets.size}</p> <p class="hidden sm:block">{$t('selected_count', { values: { count: assets.size } })}</p> diff --git a/web/src/lib/components/photos-page/delete-asset-dialog.svelte b/web/src/lib/components/photos-page/delete-asset-dialog.svelte index 84782b2d7f..3eff428a7b 100644 --- a/web/src/lib/components/photos-page/delete-asset-dialog.svelte +++ b/web/src/lib/components/photos-page/delete-asset-dialog.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import ConfirmDialog from '../shared-components/dialog/confirm-dialog.svelte'; import { showDeleteModal } from '$lib/stores/preferences.store'; import Checkbox from '$lib/components/elements/checkbox.svelte'; @@ -7,19 +6,16 @@ import FormatMessage from '$lib/components/i18n/format-message.svelte'; export let size: number; + export let onConfirm: () => void; + export let onCancel: () => void; let checked = false; - const dispatch = createEventDispatcher<{ - confirm: void; - cancel: void; - }>(); - const handleConfirm = () => { if (checked) { $showDeleteModal = false; } - dispatch('confirm'); + onConfirm(); }; </script> @@ -27,7 +23,7 @@ title={$t('permanently_delete_assets_count', { values: { count: size } })} confirmText={$t('delete')} onConfirm={handleConfirm} - onCancel={() => dispatch('cancel')} + {onCancel} > <svelte:fragment slot="prompt"> <p> diff --git a/web/src/lib/components/shared-components/album-selection-modal.svelte b/web/src/lib/components/shared-components/album-selection-modal.svelte index 0690374c01..6d28bd12c0 100644 --- a/web/src/lib/components/shared-components/album-selection-modal.svelte +++ b/web/src/lib/components/shared-components/album-selection-modal.svelte @@ -2,7 +2,7 @@ import Icon from '$lib/components/elements/icon.svelte'; import { getAllAlbums, type AlbumResponseDto } from '@immich/sdk'; import { mdiPlus } from '@mdi/js'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import AlbumListItem from '../asset-viewer/album-list-item.svelte'; import { normalizeSearchString } from '$lib/utils/string-utils'; import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; @@ -11,17 +11,15 @@ import { sortAlbums } from '$lib/utils/album-utils'; import { albumViewSettings } from '$lib/stores/preferences.store'; + export let onNewAlbum: (search: string) => void; + export let onAlbumClick: (album: AlbumResponseDto) => void; + let albums: AlbumResponseDto[] = []; let recentAlbums: AlbumResponseDto[] = []; let filteredAlbums: AlbumResponseDto[] = []; let loading = true; let search = ''; - const dispatch = createEventDispatcher<{ - newAlbum: string; - album: AlbumResponseDto; - }>(); - export let shared: boolean; export let onClose: () => void; @@ -40,14 +38,6 @@ { sortBy: $albumViewSettings.sortBy, orderBy: $albumViewSettings.sortOrder }, ); - const handleSelect = (album: AlbumResponseDto) => { - dispatch('album', album); - }; - - const handleNew = () => { - dispatch('newAlbum', search.length > 0 ? search : ''); - }; - const getTitle = () => { if (shared) { return $t('add_to_shared_album'); @@ -81,7 +71,7 @@ <div class="immich-scrollbar overflow-y-auto"> <button type="button" - on:click={handleNew} + on:click={() => onNewAlbum(search)} class="flex w-full items-center gap-4 px-6 py-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl" > <div class="flex h-12 w-12 items-center justify-center"> @@ -96,7 +86,7 @@ {#if !shared && search.length === 0} <p class="px-5 py-3 text-xs">{$t('recent').toUpperCase()}</p> {#each recentAlbums as album (album.id)} - <AlbumListItem {album} on:album={() => handleSelect(album)} /> + <AlbumListItem {album} onAlbumClick={() => onAlbumClick(album)} /> {/each} {/if} @@ -106,7 +96,7 @@ </p> {/if} {#each filteredAlbums as album (album.id)} - <AlbumListItem {album} searchQuery={search} on:album={() => handleSelect(album)} /> + <AlbumListItem {album} searchQuery={search} onAlbumClick={() => onAlbumClick(album)} /> {/each} {:else if albums.length > 0} <p class="px-5 py-1 text-sm">{$t('no_albums_with_name_yet')}</p> diff --git a/web/src/lib/components/shared-components/change-date.svelte b/web/src/lib/components/shared-components/change-date.svelte index 80eaa3d819..8ceda5f1d6 100644 --- a/web/src/lib/components/shared-components/change-date.svelte +++ b/web/src/lib/components/shared-components/change-date.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import { DateTime } from 'luxon'; import ConfirmDialog from './dialog/confirm-dialog.svelte'; import Combobox from './combobox.svelte'; @@ -8,6 +7,8 @@ export let initialDate: DateTime = DateTime.now(); export let initialTimeZone: string = ''; + export let onCancel: () => void; + export let onConfirm: (date: string) => void; type ZoneOption = { /** @@ -118,17 +119,10 @@ return zoneA.value.localeCompare(zoneB.value, undefined, { sensitivity: 'base' }); } - const dispatch = createEventDispatcher<{ - cancel: void; - confirm: string; - }>(); - - const handleCancel = () => dispatch('cancel'); - const handleConfirm = () => { const value = date.toISO(); if (value) { - dispatch('confirm', value); + onConfirm(value); } }; </script> @@ -139,7 +133,7 @@ prompt="Please select a new date:" disabled={!date.isValid} onConfirm={handleConfirm} - onCancel={handleCancel} + {onCancel} > <div class="flex flex-col text-left gap-2" slot="prompt"> <div class="flex flex-col"> diff --git a/web/src/lib/components/shared-components/change-location.svelte b/web/src/lib/components/shared-components/change-location.svelte index 3b0cb7bcc1..573c9ab38b 100644 --- a/web/src/lib/components/shared-components/change-location.svelte +++ b/web/src/lib/components/shared-components/change-location.svelte @@ -1,5 +1,4 @@ <script lang="ts"> - import { createEventDispatcher } from 'svelte'; import ConfirmDialog from './dialog/confirm-dialog.svelte'; import { timeDebounceOnSearch } from '$lib/constants'; import { handleError } from '$lib/utils/handle-error'; @@ -14,13 +13,15 @@ import { t } from 'svelte-i18n'; import CoordinatesInput from '$lib/components/shared-components/coordinates-input.svelte'; - export let asset: AssetResponseDto | undefined = undefined; - interface Point { lng: number; lat: number; } + export let asset: AssetResponseDto | undefined = undefined; + export let onCancel: () => void; + export let onConfirm: (point: Point) => void; + let places: PlacesResponseDto[] = []; let suggestedPlaces: PlacesResponseDto[] = []; let searchWord: string; @@ -30,11 +31,6 @@ let hideSuggestion = false; let addClipMapMarker: (long: number, lat: number) => void; - const dispatch = createEventDispatcher<{ - cancel: void; - confirm: Point; - }>(); - $: lat = asset?.exifInfo?.latitude ?? undefined; $: lng = asset?.exifInfo?.longitude ?? undefined; $: zoom = lat !== undefined && lng !== undefined ? 12.5 : 1; @@ -50,17 +46,11 @@ let point: Point | null = null; - const handleCancel = () => dispatch('cancel'); - - const handleSelect = (selected: Point) => { - point = selected; - }; - const handleConfirm = () => { if (point) { - dispatch('confirm', point); + onConfirm(point); } else { - dispatch('cancel'); + onCancel(); } }; @@ -108,13 +98,7 @@ }; </script> -<ConfirmDialog - confirmColor="primary" - title={$t('change_location')} - width="wide" - onConfirm={handleConfirm} - onCancel={handleCancel} -> +<ConfirmDialog confirmColor="primary" title={$t('change_location')} width="wide" onConfirm={handleConfirm} {onCancel}> <div slot="prompt" class="flex flex-col w-full h-full gap-2"> <div class="relative w-64 sm:w-96" @@ -126,10 +110,8 @@ placeholder={$t('search_places')} bind:name={searchWord} {showLoadingSpinner} - on:reset={() => { - suggestedPlaces = []; - }} - on:search={handleSearchPlaces} + onReset={() => (suggestedPlaces = [])} + onSearch={handleSearchPlaces} roundedBottom={suggestedPlaces.length === 0 || hideSuggestion} /> </button> @@ -180,7 +162,7 @@ center={lat && lng ? { lat, lng } : undefined} simplified={true} clickable={true} - on:clickedPoint={({ detail: point }) => handleSelect(point)} + onClickPoint={(selected) => (point = selected)} /> {/await} </div> diff --git a/web/src/lib/components/shared-components/combobox.svelte b/web/src/lib/components/shared-components/combobox.svelte index 7c71fe8aea..241f937be0 100644 --- a/web/src/lib/components/shared-components/combobox.svelte +++ b/web/src/lib/components/shared-components/combobox.svelte @@ -21,7 +21,7 @@ import { fly } from 'svelte/transition'; import Icon from '$lib/components/elements/icon.svelte'; import { mdiMagnify, mdiUnfoldMoreHorizontal, mdiClose } from '@mdi/js'; - import { createEventDispatcher, tick } from 'svelte'; + import { tick } from 'svelte'; import type { FormEventHandler } from 'svelte/elements'; import { shortcuts } from '$lib/actions/shortcut'; import { focusOutside } from '$lib/actions/focus-outside'; @@ -35,6 +35,7 @@ export let options: ComboBoxOption[] = []; export let selectedOption: ComboBoxOption | undefined = undefined; export let placeholder = ''; + export let onSelect: (option: ComboBoxOption | undefined) => void = () => {}; /** * Unique identifier for the combobox. @@ -61,10 +62,6 @@ searchQuery = selectedOption ? selectedOption.label : ''; } - const dispatch = createEventDispatcher<{ - select: ComboBoxOption | undefined; - }>(); - const activate = () => { isActive = true; searchQuery = ''; @@ -105,10 +102,10 @@ optionRefs[0]?.scrollIntoView({ block: 'nearest' }); }; - let onSelect = (option: ComboBoxOption) => { + let handleSelect = (option: ComboBoxOption) => { selectedOption = option; searchQuery = option.label; - dispatch('select', option); + onSelect(option); closeDropdown(); }; @@ -117,7 +114,7 @@ selectedIndex = undefined; selectedOption = undefined; searchQuery = ''; - dispatch('select', selectedOption); + onSelect(selectedOption); }; </script> @@ -188,7 +185,7 @@ shortcut: { key: 'Enter' }, onShortcut: () => { if (selectedIndex !== undefined && filteredOptions.length > 0) { - onSelect(filteredOptions[selectedIndex]); + handleSelect(filteredOptions[selectedIndex]); } closeDropdown(); }, @@ -245,7 +242,7 @@ bind:this={optionRefs[index]} class="text-left w-full px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all cursor-pointer aria-selected:bg-gray-100 aria-selected:dark:bg-gray-700" id={`${listboxId}-${index}`} - on:click={() => onSelect(option)} + on:click={() => handleSelect(option)} role="option" > {option.label} diff --git a/web/src/lib/components/shared-components/control-app-bar.svelte b/web/src/lib/components/shared-components/control-app-bar.svelte index cf128104d1..228cd88a86 100644 --- a/web/src/lib/components/shared-components/control-app-bar.svelte +++ b/web/src/lib/components/shared-components/control-app-bar.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import { browser } from '$app/environment'; - import { createEventDispatcher, onDestroy, onMount } from 'svelte'; + import { onDestroy, onMount } from 'svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import { fly } from 'svelte/transition'; import { mdiClose } from '@mdi/js'; @@ -12,13 +12,10 @@ export let backIcon = mdiClose; export let tailwindClasses = ''; export let forceDark = false; + export let onClose: () => void = () => {}; let appBarBorder = 'bg-immich-bg border border-transparent'; - const dispatch = createEventDispatcher<{ - close: void; - }>(); - const onScroll = () => { if (window.pageYOffset > 80) { appBarBorder = 'border border-gray-200 bg-gray-50 dark:border-gray-600'; @@ -33,7 +30,7 @@ const handleClose = () => { $isSelectingAllAssets = false; - dispatch('close'); + onClose(); }; onMount(() => { diff --git a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte index c50a07ad37..05fe8447a0 100644 --- a/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte +++ b/web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte @@ -7,7 +7,6 @@ import { handleError } from '$lib/utils/handle-error'; import { SharedLinkType, createSharedLink, updateSharedLink, type SharedLinkResponseDto } from '@immich/sdk'; import { mdiContentCopy, mdiLink } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import { NotificationType, notificationController } from '../notification/notification'; import SettingInputField, { SettingInputFieldType } from '../settings/setting-input-field.svelte'; import SettingSwitch from '../settings/setting-switch.svelte'; @@ -21,6 +20,7 @@ export let albumId: string | undefined = undefined; export let assetIds: string[] = []; export let editingLink: SharedLinkResponseDto | undefined = undefined; + export let onCreated: () => void = () => {}; let sharedLink: string | null = null; let description = ''; @@ -32,10 +32,6 @@ let shouldChangeExpirationTime = false; let enablePassword = false; - const dispatch = createEventDispatcher<{ - created: void; - }>(); - const expirationOptions: [number, Intl.RelativeTimeFormatUnit][] = [ [30, 'minutes'], [1, 'hour'], @@ -97,7 +93,7 @@ }, }); sharedLink = makeSharedLinkUrl($serverConfig.externalDomain, data.key); - dispatch('created'); + onCreated(); } catch (error) { handleError(error, $t('errors.failed_to_create_shared_link')); } diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index d64d784177..b595a6bb62 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -163,9 +163,9 @@ <AssetViewer asset={$viewingAsset} onAction={handleAction} - on:previous={handlePrevious} - on:next={handleNext} - on:close={() => { + onPrevious={handlePrevious} + onNext={handleNext} + onClose={() => { assetViewingStore.showAssetViewer(false); handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); }} diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte index d1da7a273b..83ea3016fd 100644 --- a/web/src/lib/components/shared-components/map/map.svelte +++ b/web/src/lib/components/shared-components/map/map.svelte @@ -13,7 +13,6 @@ import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson'; import type { GeoJSONSource, LngLatLike, StyleSpecification } from 'maplibre-gl'; import maplibregl from 'maplibre-gl'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import { AttributionControl, @@ -52,6 +51,8 @@ } export let onOpenInMapView: (() => Promise<void> | void) | undefined = undefined; + export let onSelect: (assetIds: string[]) => void = () => {}; + export let onClickPoint: ({ lat, lng }: { lat: number; lng: number }) => void = () => {}; let map: maplibregl.Map; let marker: maplibregl.Marker | null = null; @@ -62,16 +63,11 @@ key: getKey(), }) as Promise<StyleSpecification>)(); - const dispatch = createEventDispatcher<{ - selected: string[]; - clickedPoint: { lat: number; lng: number }; - }>(); - function handleAssetClick(assetId: string, map: Map | null) { if (!map) { return; } - dispatch('selected', [assetId]); + onSelect([assetId]); } async function handleClusterClick(clusterId: number, map: Map | null) { @@ -82,13 +78,13 @@ const mapSource = map?.getSource('geojson') as GeoJSONSource; const leaves = await mapSource.getClusterLeaves(clusterId, 10_000, 0); const ids = leaves.map((leaf) => leaf.properties?.id); - dispatch('selected', ids); + onSelect(ids); } function handleMapClick(event: maplibregl.MapMouseEvent) { if (clickable) { const { lng, lat } = event.lngLat; - dispatch('clickedPoint', { lng, lat }); + onClickPoint({ lng, lat }); if (marker) { marker.remove(); diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index c38893376e..bf0ca26d61 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -9,19 +9,16 @@ import { handleError } from '$lib/utils/handle-error'; import { deleteProfileImage, updateMyPreferences, type UserAvatarColor } from '@immich/sdk'; import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import { NotificationType, notificationController } from '../notification/notification'; import UserAvatar from '../user-avatar.svelte'; import AvatarSelector from './avatar-selector.svelte'; - let isShowSelectAvatar = false; + export let onLogout: () => void; + export let onClose: () => void = () => {}; - const dispatch = createEventDispatcher<{ - logout: void; - close: void; - }>(); + let isShowSelectAvatar = false; const handleSaveProfile = async (color: UserAvatarColor) => { try { @@ -75,14 +72,7 @@ </div> <div class="flex flex-col gap-1"> - <Button - href={AppRoute.USER_SETTINGS} - on:click={() => dispatch('close')} - color="dark-gray" - size="sm" - shadow={false} - border - > + <Button href={AppRoute.USER_SETTINGS} on:click={onClose} color="dark-gray" size="sm" shadow={false} border> <div class="flex place-content-center place-items-center text-center gap-2 px-2"> <Icon path={mdiCog} size="18" ariaHidden /> {$t('account_settings')} @@ -91,7 +81,7 @@ {#if $user.isAdmin} <Button href={AppRoute.ADMIN_USER_MANAGEMENT} - on:click={() => dispatch('close')} + on:click={onClose} color="dark-gray" size="sm" shadow={false} @@ -111,7 +101,7 @@ <button type="button" class="flex w-full place-content-center place-items-center gap-2 py-3 font-medium text-gray-500 hover:bg-immich-primary/10 dark:text-gray-300" - on:click={() => dispatch('logout')} + on:click={onLogout} > <Icon path={mdiLogout} size={24} /> {$t('sign_out')}</button diff --git a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte index 58a4c23d74..28f8d7bd60 100644 --- a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte @@ -10,7 +10,6 @@ import { handleLogout } from '$lib/utils/auth'; import { logout } from '@immich/sdk'; import { mdiMagnify, mdiTrayArrowUp } from '@mdi/js'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import { AppRoute } from '../../../constants'; @@ -21,13 +20,11 @@ import AccountInfoPanel from './account-info-panel.svelte'; export let showUploadButton = true; + export let onUploadClick: () => void; let shouldShowAccountInfo = false; let shouldShowAccountInfoPanel = false; let innerWidth: number; - const dispatch = createEventDispatcher<{ - uploadClicked: void; - }>(); const onLogout = async () => { const { redirectUri } = await logout(); @@ -67,14 +64,14 @@ <ThemeButton padding="2" /> {#if !$page.url.pathname.includes('/admin') && showUploadButton} - <LinkButton on:click={() => dispatch('uploadClicked')} class="hidden lg:block"> + <LinkButton on:click={onUploadClick} class="hidden lg:block"> <div class="flex gap-2"> <Icon path={mdiTrayArrowUp} size="1.5em" /> <span>{$t('upload')}</span> </div> </LinkButton> <CircleIconButton - on:click={() => dispatch('uploadClicked')} + on:click={onUploadClick} title={$t('upload')} icon={mdiTrayArrowUp} class="lg:hidden" @@ -114,7 +111,7 @@ {/if} {#if shouldShowAccountInfoPanel} - <AccountInfoPanel on:logout={onLogout} /> + <AccountInfoPanel {onLogout} /> {/if} </div> </section> diff --git a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte index 817cccac38..1c799ced11 100644 --- a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte +++ b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte @@ -8,7 +8,7 @@ <script lang="ts"> import { handlePromiseError } from '$lib/utils'; - import { createEventDispatcher, onMount } from 'svelte'; + import { onMount } from 'svelte'; import { tweened } from 'svelte/motion'; /** @@ -26,6 +26,10 @@ export let duration = 5; + export let onDone: () => void; + export let onPlaying: () => void = () => {}; + export let onPaused: () => void = () => {}; + const onChange = async () => { progress = setDuration(duration); await play(); @@ -39,16 +43,10 @@ $: { if ($progress === 1) { - dispatch('done'); + onDone(); } } - const dispatch = createEventDispatcher<{ - done: void; - playing: void; - paused: void; - }>(); - onMount(async () => { if (autoplay) { await play(); @@ -57,13 +55,13 @@ export const play = async () => { status = ProgressBarStatus.Playing; - dispatch('playing'); + onPlaying(); await progress.set(1); }; export const pause = async () => { status = ProgressBarStatus.Paused; - dispatch('paused'); + onPaused(); await progress.set($progress); }; diff --git a/web/src/lib/components/shared-components/settings/setting-dropdown.svelte b/web/src/lib/components/shared-components/settings/setting-dropdown.svelte index 5243a14931..20324fe4f8 100644 --- a/web/src/lib/components/shared-components/settings/setting-dropdown.svelte +++ b/web/src/lib/components/shared-components/settings/setting-dropdown.svelte @@ -43,7 +43,7 @@ icon: option.icon, }; }} - on:select={({ detail }) => onToggle(detail)} + onSelect={onToggle} /> </div> </div> diff --git a/web/src/lib/components/shared-components/settings/setting-select.svelte b/web/src/lib/components/shared-components/settings/setting-select.svelte index c5b9e2c02e..92cabbff25 100644 --- a/web/src/lib/components/shared-components/settings/setting-select.svelte +++ b/web/src/lib/components/shared-components/settings/setting-select.svelte @@ -1,7 +1,6 @@ <script lang="ts"> import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; - import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; import Icon from '$lib/components/elements/icon.svelte'; import { mdiChevronDown } from '@mdi/js'; @@ -14,15 +13,14 @@ export let isEdited = false; export let number = false; export let disabled = false; - - const dispatch = createEventDispatcher<{ select: string | number }>(); + export let onSelect: (setting: string | number) => void = () => {}; const handleChange = (e: Event) => { value = (e.target as HTMLInputElement).value; if (number) { value = Number.parseInt(value); } - dispatch('select', value); + onSelect(value); }; </script> diff --git a/web/src/lib/components/shared-components/settings/setting-switch.svelte b/web/src/lib/components/shared-components/settings/setting-switch.svelte index 24b539f0a1..11716526f8 100644 --- a/web/src/lib/components/shared-components/settings/setting-switch.svelte +++ b/web/src/lib/components/shared-components/settings/setting-switch.svelte @@ -1,7 +1,6 @@ <script lang="ts"> import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; - import { createEventDispatcher } from 'svelte'; import Slider from '$lib/components/elements/slider.svelte'; import { generateId } from '$lib/utils/generate-id'; import { t } from 'svelte-i18n'; @@ -11,14 +10,12 @@ export let checked = false; export let disabled = false; export let isEdited = false; + export let onToggle: (isChecked: boolean) => void = () => {}; let id: string = generateId(); $: sliderId = `${id}-slider`; $: subtitleId = subtitle ? `${id}-subtitle` : undefined; - - const dispatch = createEventDispatcher<{ toggle: boolean }>(); - const onToggle = (isChecked: boolean) => dispatch('toggle', isChecked); </script> <div class="flex place-items-center justify-between"> diff --git a/web/src/lib/components/user-settings-page/user-api-key-list.svelte b/web/src/lib/components/user-settings-page/user-api-key-list.svelte index 13ec440082..a63bdb3ca9 100644 --- a/web/src/lib/components/user-settings-page/user-api-key-list.svelte +++ b/web/src/lib/components/user-settings-page/user-api-key-list.svelte @@ -102,7 +102,7 @@ {/if} {#if secret} - <APIKeySecret {secret} on:done={() => (secret = '')} /> + <APIKeySecret {secret} onDone={() => (secret = '')} /> {/if} {#if editKey} diff --git a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte index 2f1efc487c..fd5b68d8c3 100644 --- a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte +++ b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte @@ -151,15 +151,15 @@ <AssetViewer asset={$viewingAsset} showNavigation={assets.length > 1} - on:next={() => { + onNext={() => { const index = getAssetIndex($viewingAsset.id) + 1; setAsset(assets[index % assets.length]); }} - on:previous={() => { + onPrevious={() => { const index = getAssetIndex($viewingAsset.id) - 1 + assets.length; setAsset(assets[index % assets.length]); }} - on:close={() => { + onClose={() => { assetViewingStore.showAssetViewer(false); handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); }} diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 6e75273f3b..57d09ed53a 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -674,8 +674,8 @@ disabled={!album.isActivityEnabled} {isLiked} numberOfComments={$numberOfComments} - on:favorite={handleFavorite} - on:openActivityTab={handleOpenAndCloseActivityTab} + onFavorite={handleFavorite} + onOpenActivityTab={handleOpenAndCloseActivityTab} /> </div> {/if} @@ -697,10 +697,10 @@ albumId={album.id} {isLiked} bind:reactions - on:addComment={() => updateNumberOfComments(1)} - on:deleteComment={() => updateNumberOfComments(-1)} - on:deleteLike={() => (isLiked = null)} - on:close={handleOpenAndCloseActivityTab} + onAddComment={() => updateNumberOfComments(1)} + onDeleteComment={() => updateNumberOfComments(-1)} + onDeleteLike={() => (isLiked = null)} + onClose={handleOpenAndCloseActivityTab} /> </div> </div> @@ -709,8 +709,8 @@ {#if viewMode === ViewMode.SELECT_USERS} <UserSelectionModal {album} - on:select={({ detail: users }) => handleAddUsers(users)} - on:share={() => (viewMode = ViewMode.LINK_SHARING)} + onSelect={handleAddUsers} + onShare={() => (viewMode = ViewMode.LINK_SHARING)} onClose={() => (viewMode = ViewMode.VIEW)} /> {/if} @@ -723,8 +723,8 @@ <ShareInfoModal onClose={() => (viewMode = ViewMode.VIEW)} {album} - on:remove={({ detail: userId }) => handleRemoveUser(userId)} - on:refreshAlbum={refreshAlbum} + onRemove={handleRemoveUser} + onRefreshAlbum={refreshAlbum} /> {/if} @@ -737,9 +737,9 @@ albumOrder = order; await setModeToView(); }} - on:close={() => (viewMode = ViewMode.VIEW)} - on:toggleEnableActivity={handleToggleEnableActivity} - on:showSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)} + onClose={() => (viewMode = ViewMode.VIEW)} + onToggleEnabledActivity={handleToggleEnableActivity} + onShowSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)} /> {/if} diff --git a/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/+page.svelte index 0ea0ed18bb..2e109823ed 100644 --- a/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -122,9 +122,9 @@ <AssetViewer asset={$viewingAsset} showNavigation={viewingAssets.length > 1} - on:next={navigateNext} - on:previous={navigatePrevious} - on:close={() => { + onNext={navigateNext} + onPrevious={navigatePrevious} + onClose={() => { assetViewingStore.showAssetViewer(false); handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); }} @@ -137,11 +137,11 @@ {#if showSettingsModal} <MapSettingsModal settings={{ ...$mapSettings }} - on:close={() => (showSettingsModal = false)} - on:save={async ({ detail }) => { - const shouldUpdate = !isEqual(omit(detail, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode')); + onClose={() => (showSettingsModal = false)} + onSave={async (settings) => { + const shouldUpdate = !isEqual(omit(settings, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode')); showSettingsModal = false; - $mapSettings = detail; + $mapSettings = settings; if (shouldUpdate) { mapMarkers = await loadMapMarkers(); diff --git a/web/src/routes/(user)/people/+page.svelte b/web/src/routes/(user)/people/+page.svelte index f1a2674e24..b6d25c48bf 100644 --- a/web/src/routes/(user)/people/+page.svelte +++ b/web/src/routes/(user)/people/+page.svelte @@ -302,9 +302,9 @@ {personMerge1} {personMerge2} {potentialMergePeople} - on:close={() => (showMergeModal = false)} - on:reject={() => changeName()} - on:confirm={(event) => handleMergeSamePerson(event.detail)} + onClose={() => (showMergeModal = false)} + onReject={changeName} + onConfirm={handleMergeSamePerson} /> {/if} @@ -349,10 +349,10 @@ <PeopleCard {person} preload={index < 20} - on:change-name={() => handleChangeName(person)} - on:set-birth-date={() => handleSetBirthDate(person)} - on:merge-people={() => handleMergePeople(person)} - on:hide-person={() => handleHidePerson(person)} + onChangeName={() => handleChangeName(person)} + onSetBirthDate={() => handleSetBirthDate(person)} + onMergePeople={() => handleMergePeople(person)} + onHidePerson={() => handleHidePerson(person)} /> </PeopleInfiniteScroll> {:else} @@ -397,8 +397,8 @@ {#if showSetBirthDateModal} <SetBirthDateModal birthDate={edittingPerson?.birthDate ?? ''} - on:close={() => (showSetBirthDateModal = false)} - on:updated={(event) => submitBirthDateChange(event.detail)} + onClose={() => (showSetBirthDateModal = false)} + onUpdate={submitBirthDateChange} /> {/if} </UserPageLayout> diff --git a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte index daa5821e85..bb648228b9 100644 --- a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -347,8 +347,8 @@ <UnMergeFaceSelector assetIds={[...$selectedAssets].map((a) => a.id)} personAssets={person} - on:close={() => (viewMode = ViewMode.VIEW_ASSETS)} - on:confirm={handleUnmerge} + onClose={() => (viewMode = ViewMode.VIEW_ASSETS)} + onConfirm={handleUnmerge} /> {/if} @@ -357,22 +357,22 @@ {personMerge1} {personMerge2} {potentialMergePeople} - on:close={() => (viewMode = ViewMode.VIEW_ASSETS)} - on:reject={() => changeName()} - on:confirm={(event) => handleMergeSamePerson(event.detail)} + onClose={() => (viewMode = ViewMode.VIEW_ASSETS)} + onReject={changeName} + onConfirm={handleMergeSamePerson} /> {/if} {#if viewMode === ViewMode.BIRTH_DATE} <SetBirthDateModal birthDate={person.birthDate ?? ''} - on:close={() => (viewMode = ViewMode.VIEW_ASSETS)} - on:updated={(event) => handleSetBirthDate(event.detail)} + onClose={() => (viewMode = ViewMode.VIEW_ASSETS)} + onUpdate={handleSetBirthDate} /> {/if} {#if viewMode === ViewMode.MERGE_PEOPLE} - <MergeFaceSelector {person} on:back={handleGoBack} on:merge={({ detail }) => handleMerge(detail)} /> + <MergeFaceSelector {person} onBack={handleGoBack} onMerge={handleMerge} /> {/if} <header> @@ -464,7 +464,7 @@ bind:suggestedPeople name={person.name} bind:isSearchingPeople - on:change={(event) => handleNameChange(event.detail)} + onChange={handleNameChange} {thumbnailData} /> {:else} diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte index 74db5628ba..5ce3296a03 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/+page.svelte @@ -267,10 +267,7 @@ </script> {#if toCreateLibrary} - <LibraryUserPickerForm - on:submit={({ detail }) => handleCreate(detail.ownerId)} - on:cancel={() => (toCreateLibrary = false)} - /> + <LibraryUserPickerForm onSubmit={handleCreate} onCancel={() => (toCreateLibrary = false)} /> {/if} <UserPageLayout title={data.meta.title} admin> @@ -385,28 +382,20 @@ </tr> {#if renameLibrary === index} <div transition:slide={{ duration: 250 }}> - <LibraryRenameForm - {library} - on:submit={({ detail }) => handleUpdate(detail)} - on:cancel={() => (renameLibrary = null)} - /> + <LibraryRenameForm {library} onSubmit={handleUpdate} onCancel={() => (renameLibrary = null)} /> </div> {/if} {#if editImportPaths === index} <div transition:slide={{ duration: 250 }}> - <LibraryImportPathsForm - {library} - on:submit={({ detail }) => handleUpdate(detail)} - on:cancel={() => (editImportPaths = null)} - /> + <LibraryImportPathsForm {library} onSubmit={handleUpdate} onCancel={() => (editImportPaths = null)} /> </div> {/if} {#if editScanSettings === index} <div transition:slide={{ duration: 250 }} class="mb-4 ml-4 mr-4"> <LibraryScanSettingsForm {library} - on:submit={({ detail: library }) => handleUpdate(library)} - on:cancel={() => (editScanSettings = null)} + onSubmit={handleUpdate} + onCancel={() => (editScanSettings = null)} /> </div> {/if} diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/user-management/+page.svelte index b040ce293c..2313b17cb1 100644 --- a/web/src/routes/admin/user-management/+page.svelte +++ b/web/src/routes/admin/user-management/+page.svelte @@ -110,8 +110,8 @@ <section class="w-full pb-28 lg:w-[850px]"> {#if shouldShowCreateUserForm} <CreateUserForm - on:submit={onUserCreated} - on:cancel={() => (shouldShowCreateUserForm = false)} + onSubmit={onUserCreated} + onCancel={() => (shouldShowCreateUserForm = false)} onClose={() => (shouldShowCreateUserForm = false)} /> {/if} @@ -121,8 +121,8 @@ user={selectedUser} bind:newPassword canResetPassword={selectedUser?.id !== $user.id} - on:editSuccess={onEditUserSuccess} - on:resetPasswordSuccess={onEditPasswordSuccess} + onEditSuccess={onEditUserSuccess} + onResetPasswordSuccess={onEditPasswordSuccess} onClose={() => (shouldShowEditUserForm = false)} /> {/if} diff --git a/web/src/routes/auth/change-password/+page.svelte b/web/src/routes/auth/change-password/+page.svelte index aa23e4e7d2..eaf5a88fe2 100644 --- a/web/src/routes/auth/change-password/+page.svelte +++ b/web/src/routes/auth/change-password/+page.svelte @@ -25,5 +25,5 @@ {$t('change_password_description')} </p> - <ChangePasswordForm on:success={onSuccess} /> + <ChangePasswordForm {onSuccess} /> </FullscreenContainer>