chore(web): cleanup promise handling (#7382)

* no-misused-promises

* no-floating-promises

* format

* revert for now

* remove load function

* require-await

* revert a few no-floating-promises changes that would cause no-misused-promises failures

* format

* fix a few more

* fix most remaining errors

* executor-queue

* executor-queue.spec

* remove duplicate comments by grouping rules

* upgrade sveltekit and enforce rules

* oops. move await

* try this

* just ignore for now since it's only a test

* run in parallel

* Update web/src/routes/admin/jobs-status/+page.svelte

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* remove Promise.resolve call

* rename function

* remove unnecessary warning silencing

* make handleError sync

* fix new errors from recently merged PR to main

* extract method

* use handlePromiseError

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Ben McCann 2024-02-27 08:37:37 -08:00 committed by GitHub
parent 57f25855d3
commit 907a95a746
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 312 additions and 282 deletions

View file

@ -48,11 +48,11 @@
await handleCommand(jobId, dto);
};
const onConfirm = () => {
const onConfirm = async () => {
if (!confirmJob) {
return;
}
handleCommand(confirmJob, { command: JobCommand.Start, force: true });
await handleCommand(confirmJob, { command: JobCommand.Start, force: true });
confirmJob = null;
};

View file

@ -54,7 +54,7 @@
});
};
const resetToDefault = async (configKeys: Array<keyof SystemConfigDto>) => {
const resetToDefault = (configKeys: Array<keyof SystemConfigDto>) => {
for (const key of configKeys) {
config = { ...config, [key]: defaultConfig[key] };
}

View file

@ -21,6 +21,7 @@
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
import { mdiFileImagePlusOutline, mdiFolderDownloadOutline } from '@mdi/js';
import UpdatePanel from '../shared-components/update-panel.svelte';
import { handlePromiseError } from '$lib/utils';
export let sharedLink: SharedLinkResponseDto;
export let user: UserResponseDto | undefined = undefined;
@ -35,7 +36,7 @@
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {
fileUploadHandler(value.files, album.id);
handlePromiseError(fileUploadHandler(value.files, album.id));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
}
});
@ -67,7 +68,7 @@
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
onMount(async () => {
onMount(() => {
document.addEventListener('keydown', onKeyboardPress);
});

View file

@ -1,7 +1,7 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { getAssetType } from '$lib/utils/asset-utils';
import { autoGrowHeight } from '$lib/utils/autogrow';
import { clickOutside } from '$lib/utils/click-outside';
@ -79,7 +79,7 @@
$: {
if (assetId && previousAssetId != assetId) {
getReactions();
handlePromiseError(getReactions());
previousAssetId = assetId;
}
}
@ -95,10 +95,10 @@
}
};
const handleEnter = (event: KeyboardEvent) => {
const handleEnter = async (event: KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault();
handleSendComment();
await handleSendComment();
return;
}
};

View file

@ -10,7 +10,7 @@
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
import { stackAssetsStore } from '$lib/stores/stacked-asset.store';
import { user } from '$lib/stores/user.store';
import { getAssetJobMessage, isSharedLink } from '$lib/utils';
import { getAssetJobMessage, isSharedLink, handlePromiseError } from '$lib/utils';
import { addAssetsToAlbum, downloadFile } from '$lib/utils/asset-utils';
import { handleError } from '$lib/utils/handle-error';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
@ -174,8 +174,8 @@
$: {
if (isShared && asset.id) {
getFavorite();
getNumberOfComments();
handlePromiseError(getFavorite());
handlePromiseError(getNumberOfComments());
}
}
@ -184,9 +184,9 @@
if (value === SlideshowState.PlaySlideshow) {
slideshowHistory.reset();
slideshowHistory.queue(asset.id);
handlePlaySlideshow();
handlePromiseError(handlePlaySlideshow());
} else if (value === SlideshowState.StopSlideshow) {
handleStopSlideshow();
handlePromiseError(handleStopSlideshow());
}
});
@ -226,7 +226,7 @@
}
});
$: asset.id && !sharedLink && handleGetAllAlbums(); // Update the album information when the asset ID changes
$: asset.id && !sharedLink && handlePromiseError(handleGetAllAlbums()); // Update the album information when the asset ID changes
const handleGetAllAlbums = async () => {
if (isSharedLink()) {
@ -247,7 +247,7 @@
isShowActivity = !isShowActivity;
};
const handleKeypress = (event: KeyboardEvent) => {
const handleKeypress = async (event: KeyboardEvent) => {
if (shouldIgnoreShortcut(event)) {
return;
}
@ -264,7 +264,7 @@
case 'a':
case 'A': {
if (shiftKey) {
toggleArchive();
await toggleArchive();
}
return;
}
@ -273,18 +273,18 @@
return;
}
case 'ArrowRight': {
navigateAssetForward();
await navigateAssetForward();
return;
}
case 'd':
case 'D': {
if (shiftKey) {
downloadFile(asset);
await downloadFile(asset);
}
return;
}
case 'Delete': {
trashOrDelete(shiftKey);
await trashOrDelete(shiftKey);
return;
}
case 'Escape': {
@ -296,7 +296,7 @@
return;
}
case 'f': {
toggleFavorite();
await toggleFavorite();
return;
}
case 'i': {
@ -326,7 +326,7 @@
slideshowHistory.queue(asset.id);
setAssetId(asset.id);
await setAssetId(asset.id);
$restartSlideshowProgress = true;
};
@ -369,17 +369,17 @@
$isShowDetail = !$isShowDetail;
};
const trashOrDelete = (force: boolean = false) => {
const trashOrDelete = async (force: boolean = false) => {
if (force || !isTrashEnabled) {
if ($showDeleteModal) {
isShowDeleteConfirmation = true;
return;
}
deleteAsset();
await deleteAsset();
return;
}
trashAsset();
await trashAsset();
return;
};
@ -432,7 +432,7 @@
message: asset.isFavorite ? `Added to favorites` : `Removed from favorites`,
});
} catch (error) {
await handleError(error, `Unable to ${asset.isFavorite ? `add asset to` : `remove asset from`} favorites`);
handleError(error, `Unable to ${asset.isFavorite ? `add asset to` : `remove asset from`} favorites`);
}
};
@ -472,7 +472,7 @@
message: asset.isArchived ? `Added to archive` : `Removed from archive`,
});
} catch (error) {
await handleError(error, `Unable to ${asset.isArchived ? `add asset to` : `remove asset from`} archive`);
handleError(error, `Unable to ${asset.isArchived ? `add asset to` : `remove asset from`} archive`);
}
};
@ -481,7 +481,7 @@
await runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
notificationController.show({ type: NotificationType.Info, message: getAssetJobMessage(name) });
} catch (error) {
await handleError(error, `Unable to submit job`);
handleError(error, `Unable to submit job`);
}
};
@ -492,7 +492,7 @@
let assetViewerHtmlElement: HTMLElement;
const slideshowHistory = new SlideshowHistory((assetId: string) => {
setAssetId(assetId);
handlePromiseError(setAssetId(assetId));
$restartSlideshowProgress = true;
});
@ -550,7 +550,7 @@
dispatch('close');
notificationController.show({ type: NotificationType.Info, message: 'Un-stacked', timeout: 1500 });
} catch (error) {
await handleError(error, `Unable to unstack`);
handleError(error, `Unable to unstack`);
}
};
</script>

View file

@ -7,7 +7,7 @@
import { featureFlags } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store';
import { websocketEvents } from '$lib/stores/websocket';
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink, handlePromiseError } from '$lib/utils';
import { delay, getAssetFilename } from '$lib/utils/asset-utils';
import { autoGrowHeight } from '$lib/utils/autogrow';
import { clickOutside } from '$lib/utils/click-outside';
@ -78,7 +78,7 @@
originalDescription = description;
};
$: handleNewAsset(asset);
$: handlePromiseError(handleNewAsset(asset));
$: latlng = (() => {
const lat = asset.exifInfo?.latitude;
@ -113,7 +113,7 @@
switch (event.key) {
case 'Enter': {
if (ctrl && event.target === textArea) {
handleFocusOut();
await handleFocusOut();
}
}
}

View file

@ -4,7 +4,7 @@
import { boundingBoxesArray } from '$lib/stores/people.store';
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
import { photoZoomState } from '$lib/stores/zoom-image.store';
import { getKey } from '$lib/utils';
import { getKey, handlePromiseError } from '$lib/utils';
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { getBoundingBox } from '$lib/utils/people-utils';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
@ -102,7 +102,7 @@
}
};
const doZoomImage = async () => {
const doZoomImage = () => {
setZoomImageWheelState({
currentZoom: $zoomImageWheelState.currentZoom === 1 ? 2 : 1,
});
@ -120,7 +120,7 @@
if (state.currentZoom > 1 && isWebCompatibleImage(asset) && !hasZoomed && !$alwaysLoadOriginalFile) {
hasZoomed = true;
loadAssetData({ loadOriginal: true });
handlePromiseError(loadAssetData({ loadOriginal: true }));
}
});
</script>

View file

@ -20,7 +20,7 @@
video.muted = false;
dispatch('onVideoStarted');
} catch (error) {
await handleError(error, 'Unable to play video');
handleError(error, 'Unable to play video');
} finally {
isVideoLoading = false;
}

View file

@ -49,7 +49,7 @@
if (assetType === AssetTypeEnum.Image) {
image = $photoViewer;
} else if (assetType === AssetTypeEnum.Video) {
const data = await getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
const data = getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
const img: HTMLImageElement = new Image();
img.src = data;

View file

@ -43,10 +43,10 @@
dispatch('back');
};
const handleSwapPeople = () => {
const handleSwapPeople = async () => {
[person, selectedPeople[0]] = [selectedPeople[0], person];
$page.url.searchParams.set(QueryParameter.ACTION, ActionQueryParameterValue.MERGE);
goto(`${AppRoute.PEOPLE}/${person.id}?${$page.url.searchParams.toString()}`);
await goto(`${AppRoute.PEOPLE}/${person.id}?${$page.url.searchParams.toString()}`);
};
const onSelect = (selected: PersonResponseDto) => {

View file

@ -3,7 +3,7 @@
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
import { boundingBoxesArray } from '$lib/stores/people.store';
import { websocketEvents } from '$lib/stores/websocket';
import { getPeopleThumbnailUrl } from '$lib/utils';
import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
import {
@ -85,7 +85,7 @@
};
onMount(() => {
loadPeople();
handlePromiseError(loadPeople());
return websocketEvents.on('on_person_thumbnail', onPersonThumbnail);
});
@ -164,7 +164,7 @@
}
};
const handlePersonPicker = async (index: number) => {
const handlePersonPicker = (index: number) => {
editedPersonIndex = index;
showSeletecFaces = true;
};

View file

@ -132,9 +132,7 @@
title={'Assign selected assets to a new person'}
size={'sm'}
disabled={disableButtons || hasSelection}
on:click={() => {
handleCreate();
}}
on:click={handleCreate}
>
{#if !showLoadingSpinnerCreate}
<Icon path={mdiPlus} size={18} />
@ -147,9 +145,7 @@
size={'sm'}
title={'Assign selected assets to an existing person'}
disabled={disableButtons || !hasSelection}
on:click={() => {
handleReassign();
}}
on:click={handleReassign}
>
{#if !showLoadingSpinnerReassign}
<div>

View file

@ -37,7 +37,7 @@
dispatch('submit', { library, type: LibraryType.External });
};
const handleAddExclusionPattern = async () => {
const handleAddExclusionPattern = () => {
if (!addExclusionPattern) {
return;
}
@ -60,7 +60,7 @@
}
};
const handleEditExclusionPattern = async () => {
const handleEditExclusionPattern = () => {
if (editExclusionPattern === null) {
return;
}
@ -79,7 +79,7 @@
}
};
const handleDeleteExclusionPattern = async () => {
const handleDeleteExclusionPattern = () => {
if (editExclusionPattern === null) {
return;
}

View file

@ -47,7 +47,7 @@
return;
}
} catch (error) {
await handleError(error, 'Unable to connect!');
handleError(error, 'Unable to connect!');
}
oauthLoading = false;

View file

@ -8,7 +8,7 @@
import { AppRoute, QueryParameter } from '$lib/constants';
import type { Viewport } from '$lib/stores/assets.store';
import { memoryStore } from '$lib/stores/memory.store';
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { fromLocalDateTime } from '$lib/utils/timeline-util';
import { ThumbnailFormat, getMemoryLane } from '@immich/sdk';
import { mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiPause, mdiPlay } from '@mdi/js';
@ -59,30 +59,30 @@
let paused = false;
// Play or pause progress when the paused state changes.
$: paused ? pause() : play();
$: paused ? handlePromiseError(pause()) : handlePromiseError(play());
// Progress should be paused when it's no longer possible to advance.
$: paused ||= !canGoForward || galleryInView;
// Advance to the next asset or memory when progress is complete.
$: $progress === 1 && toNext();
$: $progress === 1 && handlePromiseError(toNext());
// Progress should be resumed when reset and not paused.
$: !$progress && !paused && play();
$: !$progress && !paused && handlePromiseError(play());
// Progress should be reset when the current memory or asset changes.
$: memoryIndex, assetIndex, reset();
$: memoryIndex, assetIndex, handlePromiseError(reset());
const handleKeyDown = (e: KeyboardEvent) => {
const handleKeyDown = async (e: KeyboardEvent) => {
if (e.key === 'ArrowRight' && canGoForward) {
e.preventDefault();
toNext();
await toNext();
} else if (e.key === 'ArrowLeft' && canGoBack) {
e.preventDefault();
toPrevious();
await toPrevious();
} else if (e.key === 'Escape') {
e.preventDefault();
goto(AppRoute.PHOTOS);
await goto(AppRoute.PHOTOS);
}
};

View file

@ -27,18 +27,22 @@
showAlbumPicker = false;
const assetIds = [...getAssets()].map((asset) => asset.id);
createAlbum({ createAlbumDto: { albumName, assetIds } }).then((response) => {
const { id, albumName } = response;
createAlbum({ createAlbumDto: { albumName, assetIds } })
.then(async (response) => {
const { id, albumName } = response;
notificationController.show({
message: `Added ${assetIds.length} to ${albumName}`,
type: NotificationType.Info,
notificationController.show({
message: `Added ${assetIds.length} to ${albumName}`,
type: NotificationType.Info,
});
clearSelect();
await goto(`${AppRoute.ALBUMS}/${id}`);
})
.catch((error) => {
console.error(`[add-to-album.svelte]:handleAddToNewAlbum ${error}`, error);
});
clearSelect();
goto(`${AppRoute.ALBUMS}/${id}`);
});
};
const handleAddToAlbum = async (album: AlbumResponseDto) => {

View file

@ -80,13 +80,17 @@
});
}
const assetClickHandler = (asset: AssetResponseDto, assetsInDateGroup: AssetResponseDto[], groupTitle: string) => {
const assetClickHandler = async (
asset: AssetResponseDto,
assetsInDateGroup: AssetResponseDto[],
groupTitle: string,
) => {
if (isSelectionMode || $isMultiSelectState) {
assetSelectHandler(asset, assetsInDateGroup, groupTitle);
return;
}
assetViewingStore.setAssetId(asset.id);
await assetViewingStore.setAssetId(asset.id);
};
const handleSelectGroup = (title: string, assets: AssetResponseDto[]) => dispatch('select', { title, assets });

View file

@ -21,6 +21,7 @@
import ShowShortcuts from '../shared-components/show-shortcuts.svelte';
import AssetDateGroup from './asset-date-group.svelte';
import DeleteAssetDialog from './delete-asset-dialog.svelte';
import { handlePromiseError } from '$lib/utils';
export let isSelectionMode = false;
export let singleSelect = false;
@ -47,19 +48,19 @@
$: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0;
$: idsSelectedAssets = [...$selectedAssets].filter((a) => !a.isExternal).map((a) => a.id);
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>();
const onKeydown = (event: KeyboardEvent) => handlePromiseError(handleKeyboardPress(event));
onMount(async () => {
showSkeleton = false;
document.addEventListener('keydown', onKeyboardPress);
document.addEventListener('keydown', onKeydown);
assetStore.connect();
await assetStore.init(viewport);
});
onDestroy(() => {
if (browser) {
document.removeEventListener('keydown', onKeyboardPress);
document.removeEventListener('keydown', onKeydown);
}
if ($showAssetViewer) {
@ -69,13 +70,13 @@
assetStore.disconnect();
});
const trashOrDelete = (force: boolean = false) => {
const trashOrDelete = async (force: boolean = false) => {
isShowDeleteConfirmation = false;
deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
await deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
assetInteractionStore.clearMultiselect();
};
const handleKeyboardPress = (event: KeyboardEvent) => {
const handleKeyboardPress = async (event: KeyboardEvent) => {
if ($isSearchEnabled || shouldIgnoreShortcut(event)) {
return;
}
@ -98,7 +99,7 @@
}
case '/': {
event.preventDefault();
goto(AppRoute.EXPLORE);
await goto(AppRoute.EXPLORE);
return;
}
case 'Delete': {
@ -112,7 +113,7 @@
force = true;
}
trashOrDelete(force);
await trashOrDelete(force);
}
return;
}
@ -126,12 +127,12 @@
}
};
function intersectedHandler(event: CustomEvent) {
async function intersectedHandler(event: CustomEvent) {
const element_ = event.detail.container as HTMLElement;
const target = element_.firstChild as HTMLElement;
if (target) {
const bucketDate = target.id.split('_')[1];
assetStore.loadBucket(bucketDate, event.detail.position);
await assetStore.loadBucket(bucketDate, event.detail.position);
}
}
@ -142,7 +143,7 @@
const handlePrevious = async () => {
const previousAsset = await assetStore.getPreviousAssetId($viewingAsset.id);
if (previousAsset) {
assetViewingStore.setAssetId(previousAsset);
await assetViewingStore.setAssetId(previousAsset);
}
return !!previousAsset;
@ -151,7 +152,7 @@
const handleNext = async () => {
const nextAsset = await assetStore.getNextAssetId($viewingAsset.id);
if (nextAsset) {
assetViewingStore.setAssetId(nextAsset);
await assetViewingStore.setAssetId(nextAsset);
}
return !!nextAsset;
@ -369,7 +370,7 @@
<DeleteAssetDialog
size={idsSelectedAssets.length}
on:cancel={() => (isShowDeleteConfirmation = false)}
on:confirm={() => trashOrDelete(true)}
on:confirm={() => handlePromiseError(trashOrDelete(true))}
/>
{/if}

View file

@ -2,7 +2,7 @@
import { goto } from '$app/navigation';
import { AppRoute } from '$lib/constants';
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
import { getKey } from '$lib/utils';
import { getKey, handlePromiseError } from '$lib/utils';
import { downloadArchive } from '$lib/utils/asset-utils';
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
import { handleError } from '$lib/utils/handle-error';
@ -29,7 +29,7 @@
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {
handleUploadAssets(value.files);
handlePromiseError(handleUploadAssets(value.files));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
}
});
@ -59,7 +59,7 @@
type: NotificationType.Info,
});
} catch (error) {
await handleError(error, 'Unable to add assets to shared link');
handleError(error, 'Unable to add assets to shared link');
}
};

View file

@ -2,7 +2,7 @@
import Icon from '$lib/components/elements/icon.svelte';
import { Theme } from '$lib/constants';
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { getMapStyle, MapTheme, type MapMarkerResponseDto } from '@immich/sdk';
import { mdiCog, mdiMapMarker } from '@mdi/js';
import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson';
@ -152,9 +152,7 @@
applyToClusters
asButton
let:feature
on:click={(event) => {
handleClusterClick(event.detail.feature.properties.cluster_id, map);
}}
on:click={(event) => handlePromiseError(handleClusterClick(event.detail.feature.properties.cluster_id, map))}
>
<div
class="rounded-full w-[40px] h-[40px] bg-immich-primary text-immich-gray flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"

View file

@ -8,8 +8,8 @@
easing: cubicOut,
});
onMount(() => {
progress.set(90);
onMount(async () => {
await progress.set(90);
});
</script>

View file

@ -1,4 +1,5 @@
<script context="module" lang="ts">
import { handlePromiseError } from '$lib/utils';
import { tick } from 'svelte';
/**
@ -36,7 +37,7 @@
}
}
update(target);
handlePromiseError(update(target));
return {
update,
destroy,

View file

@ -6,6 +6,8 @@
</script>
<script lang="ts">
import { handlePromiseError } from '$lib/utils';
import { createEventDispatcher, onMount } from 'svelte';
import { tweened } from 'svelte/motion';
@ -24,14 +26,14 @@
export let duration = 5;
const onChange = () => {
const onChange = async () => {
progress = setDuration(duration);
play();
await play();
};
let progress = setDuration(duration);
$: duration, onChange();
$: duration, handlePromiseError(onChange());
$: {
if ($progress === 1) {
@ -45,35 +47,35 @@
paused: void;
}>();
onMount(() => {
onMount(async () => {
if (autoplay) {
play();
await play();
}
});
export const play = () => {
export const play = async () => {
status = ProgressBarStatus.Playing;
dispatch('playing');
progress.set(1);
await progress.set(1);
};
export const pause = () => {
export const pause = async () => {
status = ProgressBarStatus.Paused;
dispatch('paused');
progress.set($progress);
await progress.set($progress);
};
export const restart = (autoplay: boolean) => {
progress.set(0);
export const restart = async (autoplay: boolean) => {
await progress.set(0);
if (autoplay) {
play();
await play();
}
};
export const reset = () => {
export const reset = async () => {
status = ProgressBarStatus.Paused;
progress.set(0);
await progress.set(0);
};
function setDuration(newDuration: number) {

View file

@ -10,6 +10,7 @@
import SearchFilterBox from './search-filter-box.svelte';
import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk';
import { getMetadataSearchQuery } from '$lib/utils/metadata-search';
import { handlePromiseError } from '$lib/utils';
export let value = '';
export let grayTheme: boolean;
@ -21,13 +22,13 @@
let showFilter = false;
$: showClearIcon = value.length > 0;
const onSearch = (payload: SmartSearchDto | MetadataSearchDto) => {
const onSearch = async (payload: SmartSearchDto | MetadataSearchDto) => {
const params = getMetadataSearchQuery(payload);
showHistory = false;
showFilter = false;
$isSearchEnabled = false;
goto(`${AppRoute.SEARCH}?${params}`);
await goto(`${AppRoute.SEARCH}?${params}`);
};
const clearSearchTerm = (searchTerm: string) => {
@ -63,9 +64,9 @@
showFilter = false;
};
const onHistoryTermClick = (searchTerm: string) => {
const onHistoryTermClick = async (searchTerm: string) => {
const searchPayload = { query: searchTerm };
onSearch(searchPayload);
await onSearch(searchPayload);
};
const onFilterClick = () => {
@ -78,7 +79,7 @@
};
const onSubmit = () => {
onSearch({ query: value });
handlePromiseError(onSearch({ query: value }));
saveSearchTerm(value);
};
</script>
@ -141,7 +142,7 @@
<SearchHistoryBox
on:clearAllSearchTerms={clearAllSearchTerms}
on:clearSearchTerm={({ detail: searchTerm }) => clearSearchTerm(searchTerm)}
on:selectSearchTerm={({ detail: searchTerm }) => onHistoryTermClick(searchTerm)}
on:selectSearchTerm={({ detail: searchTerm }) => handlePromiseError(onHistoryTermClick(searchTerm))}
/>
{/if}
</form>

View file

@ -8,6 +8,7 @@
<script lang="ts">
import { SearchSuggestionType, getSearchSuggestions } from '@immich/sdk';
import Combobox, { toComboBoxOptions } from '../combobox.svelte';
import { handlePromiseError } from '$lib/utils';
export let filters: SearchCameraFilter;
@ -16,8 +17,8 @@
$: makeFilter = filters.make;
$: modelFilter = filters.model;
$: updateMakes(modelFilter);
$: updateModels(makeFilter);
$: handlePromiseError(updateMakes(modelFilter));
$: handlePromiseError(updateModels(makeFilter));
async function updateMakes(model?: string) {
makes = await getSearchSuggestions({

View file

@ -82,7 +82,7 @@
};
};
const search = async () => {
const search = () => {
if (filter.context && filter.personIds.size > 0) {
handleError(
new Error('Context search does not support people filter'),

View file

@ -9,6 +9,7 @@
<script lang="ts">
import { getSearchSuggestions, SearchSuggestionType } from '@immich/sdk';
import Combobox, { toComboBoxOptions } from '../combobox.svelte';
import { handlePromiseError } from '$lib/utils';
export let filters: SearchLocationFilter;
@ -18,9 +19,9 @@
$: countryFilter = filters.country;
$: stateFilter = filters.state;
$: updateCountries();
$: updateStates(countryFilter);
$: updateCities(countryFilter, stateFilter);
$: handlePromiseError(updateCountries());
$: handlePromiseError(updateStates(countryFilter));
$: handlePromiseError(updateCities(countryFilter, stateFilter));
async function updateCountries() {
countries = await getSearchSuggestions({

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/stores';
import { QueryParameter } from '$lib/constants';
import { hasParamValue, updateParamList } from '$lib/utils';
import { hasParamValue, handlePromiseError, updateParamList } from '$lib/utils';
import { slide } from 'svelte/transition';
export let title: string;
@ -12,12 +12,12 @@
const syncFromUrl = () => (isOpen = hasParamValue(QueryParameter.IS_OPEN, key));
const syncToUrl = (isOpen: boolean) => updateParamList({ param: QueryParameter.IS_OPEN, value: key, add: isOpen });
isOpen ? syncToUrl(true) : syncFromUrl();
isOpen ? handlePromiseError(syncToUrl(true)) : syncFromUrl();
$: $page.url && syncFromUrl();
const toggle = () => {
const toggle = async () => {
isOpen = !isOpen;
syncToUrl(isOpen);
await syncToUrl(isOpen);
};
</script>

View file

@ -13,9 +13,9 @@
export let uploadAsset: UploadAsset;
const handleRetry = (uploadAsset: UploadAsset) => {
const handleRetry = async (uploadAsset: UploadAsset) => {
uploadAssetsStore.removeUploadAsset(uploadAsset.id);
fileUploadHandler([uploadAsset.file], uploadAsset.albumId);
await fileUploadHandler([uploadAsset.file], uploadAsset.albumId);
};
</script>

View file

@ -56,8 +56,8 @@
let selectedLibraryIndex = 0;
let selectedLibrary: LibraryResponseDto | null = null;
onMount(() => {
readLibraryList();
onMount(async () => {
await readLibraryList();
});
const closeAll = () => {
@ -234,11 +234,11 @@
updateLibraryIndex = selectedLibraryIndex;
};
const onScanNewLibraryClicked = () => {
const onScanNewLibraryClicked = async () => {
closeAll();
if (selectedLibrary) {
handleScan(selectedLibrary.id);
await handleScan(selectedLibrary.id);
}
};
@ -248,38 +248,38 @@
updateLibraryIndex = selectedLibraryIndex;
};
const onScanAllLibraryFilesClicked = () => {
const onScanAllLibraryFilesClicked = async () => {
closeAll();
if (selectedLibrary) {
handleScanChanges(selectedLibrary.id);
await handleScanChanges(selectedLibrary.id);
}
};
const onForceScanAllLibraryFilesClicked = () => {
const onForceScanAllLibraryFilesClicked = async () => {
closeAll();
if (selectedLibrary) {
handleForceScan(selectedLibrary.id);
await handleForceScan(selectedLibrary.id);
}
};
const onRemoveOfflineFilesClicked = () => {
const onRemoveOfflineFilesClicked = async () => {
closeAll();
if (selectedLibrary) {
handleRemoveOffline(selectedLibrary.id);
await handleRemoveOffline(selectedLibrary.id);
}
};
const onDeleteLibraryClicked = () => {
const onDeleteLibraryClicked = async () => {
closeAll();
if (selectedLibrary && confirm(`Are you sure you want to delete ${selectedLibrary.name} library?`) == true) {
refreshStats(selectedLibraryIndex);
await refreshStats(selectedLibraryIndex);
if (totalCount[selectedLibraryIndex] > 0) {
deleteAssetCount = totalCount[selectedLibraryIndex];
confirmDeleteLibrary = selectedLibrary;
} else {
deletedLibrary = selectedLibrary;
handleDelete();
await handleDelete();
}
}
};
@ -348,27 +348,27 @@
{#if showContextMenu}
<Portal target="body">
<ContextMenu {...contextMenuPosition} on:outclick={() => onMenuExit()}>
<MenuOption on:click={() => onRenameClicked()} text={`Rename`} />
<ContextMenu {...contextMenuPosition} on:outclick={onMenuExit}>
<MenuOption on:click={onRenameClicked} text={`Rename`} />
{#if selectedLibrary && selectedLibrary.type === LibraryType.External}
<MenuOption on:click={() => onEditImportPathClicked()} text="Edit Import Paths" />
<MenuOption on:click={() => onScanSettingClicked()} text="Scan Settings" />
<MenuOption on:click={onEditImportPathClicked} text="Edit Import Paths" />
<MenuOption on:click={onScanSettingClicked} text="Scan Settings" />
<hr />
<MenuOption on:click={() => onScanNewLibraryClicked()} text="Scan New Library Files" />
<MenuOption on:click={onScanNewLibraryClicked} text="Scan New Library Files" />
<MenuOption
on:click={() => onScanAllLibraryFilesClicked()}
on:click={onScanAllLibraryFilesClicked}
text="Re-scan All Library Files"
subtitle={'Only refreshes modified files'}
/>
<MenuOption
on:click={() => onForceScanAllLibraryFilesClicked()}
on:click={onForceScanAllLibraryFilesClicked}
text="Force Re-scan All Library Files"
subtitle={'Refreshes every file'}
/>
<hr />
<MenuOption on:click={() => onRemoveOfflineFilesClicked()} text="Remove Offline Files" />
<MenuOption on:click={() => onDeleteLibraryClicked()}>
<MenuOption on:click={onRemoveOfflineFilesClicked} text="Remove Offline Files" />
<MenuOption on:click={onDeleteLibraryClicked}>
<p class="text-red-600">Delete library</p>
</MenuOption>
{/if}

View file

@ -28,7 +28,7 @@
} catch (error) {
handleError(error, 'Unable to link OAuth account');
} finally {
goto('?open=oauth');
await goto('?open=oauth');
}
}

View file

@ -31,8 +31,8 @@
let removePartnerDto: PartnerResponseDto | null = null;
let partners: Array<PartnerSharing> = [];
onMount(() => {
refreshPartners();
onMount(async () => {
await refreshPartners();
});
const refreshPartners = async () => {