chore(deps): update dependency eslint-plugin-unicorn to v70 - abandoned (#29684)

* chore(deps): update dependency eslint-plugin-unicorn to v70

* fix: linting

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
This commit is contained in:
renovate[bot]
2026-07-20 23:47:14 -04:00
committed by GitHub
co-authored by renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Daniel Dietzler
parent 8061a2e5ff
commit df970da59e
266 changed files with 1260 additions and 1212 deletions
+28 -3
View File
@@ -42,7 +42,7 @@ export default typescriptEslint.config(
tsconfigRootDir: __dirname,
},
},
ignores: ['**/service-worker/**'],
// ignores: ['**/service-worker/**'],
},
{
plugins: {
@@ -116,7 +116,7 @@ export default typescriptEslint.config(
'unicorn/no-useless-undefined': 'off',
'unicorn/prefer-spread': 'off',
'unicorn/no-null': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/name-replacements': 'off',
'unicorn/no-nested-ternary': 'off',
'unicorn/consistent-function-scoping': 'off',
'unicorn/filename-case': 'off',
@@ -124,12 +124,37 @@ export default typescriptEslint.config(
'unicorn/import-style': 'off',
'unicorn/no-array-sort': 'off',
'unicorn/no-for-loop': 'off',
'svelte/button-has-type': 'error',
'unicorn/no-unreadable-for-of-expression': 'off',
'unicorn/no-break-in-nested-loop': 'off',
'unicorn/no-top-level-assignment-in-function': 'off',
'unicorn/prefer-uint8array-base64': 'off',
'unicorn/max-nested-calls': 'off',
'unicorn/no-declarations-before-early-exit': 'off',
'unicorn/no-unreadable-object-destructuring': 'off',
// not yet compatible with all our supported browsers
'unicorn/prefer-promise-with-resolvers': 'off',
// not yet compatible with all our supported browsers
'unicorn/prefer-iterator-to-array': 'off',
// not yet compatible with all our supported browsers
'unicorn/prefer-array-from-async': 'off',
// maybe we do want to enable this later. TBD
'unicorn/prefer-await': 'off',
'unicorn/consistent-class-member-order': 'off',
'unicorn/class-reference-in-static-methods': ['error', { preferThis: false, preferSuper: false }],
'unicorn/no-unsafe-property-key': 'off',
'unicorn/consistent-boolean-name': 'off',
'unicorn/no-non-function-verb-prefix': 'off',
'unicorn/prefer-minimal-ternary': 'off',
'unicorn/no-empty-file': 'off',
// prefer the typescript-eslint type-aware version
'unicorn/require-array-sort-compare': 'off',
'@typescript-eslint/require-array-sort-compare': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/require-await': 'error',
'@typescript-eslint/switch-exhaustiveness-check': ['error', { considerDefaultExhaustiveForUnions: true }],
'svelte/button-has-type': 'error',
'object-shorthand': ['error', 'always'],
'svelte/no-navigation-without-resolve': 'off',
},
+1 -1
View File
@@ -97,7 +97,7 @@
"eslint-plugin-better-tailwindcss": "^4.5.0",
"eslint-plugin-compat": "^7.0.0",
"eslint-plugin-svelte": "^3.12.4",
"eslint-plugin-unicorn": "^64.0.0",
"eslint-plugin-unicorn": "^70.0.0",
"factory.ts": "^1.4.1",
"globals": "^17.0.0",
"happy-dom": "^20.0.0",
+1 -1
View File
@@ -6,7 +6,7 @@ import GoogleSansCode from '$lib/assets/fonts/GoogleSansCode/GoogleSansCode.ttf?
export const handle = (async ({ event, resolve }) => {
return resolve(event, {
transformPageChunk: ({ html }) => {
return html.replace('%app.font%', GoogleSans).replace('%app.monofont%', GoogleSansCode);
return html.replace('%app.font%', () => GoogleSans).replace('%app.monofont%', () => GoogleSansCode);
},
});
}) satisfies Handle;
+1
View File
@@ -1,5 +1,6 @@
const createObjectURLMock = vi.fn();
// eslint-disable-next-line unicorn/no-top-level-side-effects
Object.defineProperty(URL, 'createObjectURL', {
writable: true,
value: createObjectURLMock,
+1
View File
@@ -1,6 +1,7 @@
import * as sdk from '@immich/sdk';
import type { Mock, MockedObject } from 'vitest';
// eslint-disable-next-line unicorn/no-top-level-side-effects
vi.mock('@immich/sdk', async (originalImport) => {
const module = await originalImport<typeof import('@immich/sdk')>();
+2 -2
View File
@@ -35,8 +35,8 @@ export function clickOutside(node: HTMLElement, options: Options = {}): ActionRe
}
};
document.addEventListener('mousedown', handleClick, false);
node.addEventListener('keydown', handleKey, false);
document.addEventListener('mousedown', handleClick, { capture: false });
node.addEventListener('keydown', handleKey, { capture: false });
return {
destroy() {
-118
View File
@@ -1,118 +0,0 @@
export interface DragAndDropOptions {
index: number;
onDragStart?: (index: number) => void;
onDragEnter?: (index: number) => void;
onDrop?: (e: DragEvent, index: number) => void;
onDragEnd?: () => void;
isDragging?: boolean;
isDragOver?: boolean;
}
export function dragAndDrop(node: HTMLElement, options: DragAndDropOptions) {
let { index, onDragStart, onDragEnter, onDrop, onDragEnd, isDragging, isDragOver } = options;
const isFormElement = (element: HTMLElement) => {
return element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT';
};
const handleDragStart = (e: DragEvent) => {
// Prevent drag if it originated from an input, textarea, or select element
const target = e.target as HTMLElement;
if (isFormElement(target)) {
e.preventDefault();
return;
}
onDragStart?.(index);
};
const handleDragEnter = () => {
onDragEnter?.(index);
};
const handleDragOver = (e: DragEvent) => {
e.preventDefault();
};
const handleDrop = (e: DragEvent) => {
onDrop?.(e, index);
};
const handleDragEnd = () => {
onDragEnd?.();
};
// Disable draggable when focusing on form elements (fixes Firefox input interaction)
const handleFocusIn = (e: FocusEvent) => {
const target = e.target as HTMLElement;
if (isFormElement(target)) {
node.setAttribute('draggable', 'false');
}
};
const handleFocusOut = (e: FocusEvent) => {
const target = e.target as HTMLElement;
if (isFormElement(target)) {
node.setAttribute('draggable', 'true');
}
};
node.setAttribute('draggable', 'true');
node.setAttribute('role', 'button');
node.setAttribute('tabindex', '0');
node.addEventListener('dragstart', handleDragStart);
node.addEventListener('dragenter', handleDragEnter);
node.addEventListener('dragover', handleDragOver);
node.addEventListener('drop', handleDrop);
node.addEventListener('dragend', handleDragEnd);
node.addEventListener('focusin', handleFocusIn);
node.addEventListener('focusout', handleFocusOut);
// Update classes based on drag state
const updateClasses = (dragging: boolean, dragOver: boolean) => {
// Remove all drag-related classes first
node.classList.remove('opacity-50', 'border-gray-400', 'dark:border-gray-500', 'border-solid');
// Add back only the active ones
if (dragging) {
node.classList.add('opacity-50');
}
if (dragOver) {
node.classList.add('border-gray-400', 'dark:border-gray-500', 'border-solid');
node.classList.remove('border-transparent');
} else {
node.classList.add('border-transparent');
}
};
updateClasses(isDragging || false, isDragOver || false);
return {
update(newOptions: DragAndDropOptions) {
index = newOptions.index;
onDragStart = newOptions.onDragStart;
onDragEnter = newOptions.onDragEnter;
onDrop = newOptions.onDrop;
onDragEnd = newOptions.onDragEnd;
const newIsDragging = newOptions.isDragging || false;
const newIsDragOver = newOptions.isDragOver || false;
if (newIsDragging !== isDragging || newIsDragOver !== isDragOver) {
isDragging = newIsDragging;
isDragOver = newIsDragOver;
updateClasses(isDragging, isDragOver);
}
},
destroy() {
node.removeEventListener('dragstart', handleDragStart);
node.removeEventListener('dragenter', handleDragEnter);
node.removeEventListener('dragover', handleDragOver);
node.removeEventListener('drop', handleDrop);
node.removeEventListener('dragend', handleDragEnd);
node.removeEventListener('focusin', handleFocusIn);
node.removeEventListener('focusout', handleFocusOut);
},
};
}
+29 -23
View File
@@ -69,38 +69,44 @@ export function focusTrap(container: HTMLElement, options?: Options) {
// Add focus event listeners to sentinel nodes
const handleStartFocus = () => {
if (withDefaults(options).active) {
const [, lastElement] = getFocusableElements();
// If no elements, stay on backup sentinel
if (lastElement) {
lastElement.focus();
} else {
backupSentinel.focus();
}
if (!withDefaults(options).active) {
return;
}
const [, lastElement] = getFocusableElements();
// If no elements, stay on backup sentinel
if (lastElement) {
lastElement.focus();
} else {
backupSentinel.focus();
}
};
const handleBackupFocus = () => {
// Backup sentinel keeps focus when there are no other focusable elements
if (withDefaults(options).active) {
const [firstElement] = getFocusableElements();
// Only move focus if there are actual focusable elements
if (firstElement) {
firstElement.focus();
}
// Otherwise, focus stays on backup sentinel
if (!withDefaults(options).active) {
return;
}
const [firstElement] = getFocusableElements();
// Only move focus if there are actual focusable elements
if (firstElement) {
firstElement.focus();
}
// Otherwise, focus stays on backup sentinel
};
const handleEndFocus = () => {
if (withDefaults(options).active) {
const [firstElement] = getFocusableElements();
// If no elements, move to backup sentinel
if (firstElement) {
firstElement.focus();
} else {
backupSentinel.focus();
}
if (!withDefaults(options).active) {
return;
}
const [firstElement] = getFocusableElements();
// If no elements, move to backup sentinel
if (firstElement) {
firstElement.focus();
} else {
backupSentinel.focus();
}
};
+6 -4
View File
@@ -52,7 +52,7 @@ export function scrollMemory(
const newScroll = sessionStorage.getItem(SessionStorageKey.SCROLL_POSITION);
if (newScroll) {
node.scroll({
top: Number.parseFloat(newScroll),
top: Number(newScroll),
behavior: 'instant',
});
}
@@ -71,10 +71,12 @@ export function scrollMemory(
export function scrollMemoryClearer(_node: HTMLElement, { routeStartsWith, beforeClear }: Options) {
const unsubscribeNavigating = navigating.subscribe((navigation) => {
// Forget scroll position from main page if going somewhere else.
if (navigation?.to && !navigation?.to.url.pathname.startsWith(routeStartsWith)) {
beforeClear?.();
sessionStorage.removeItem(SessionStorageKey.SCROLL_POSITION);
if (!navigation?.to || navigation?.to.url.pathname.startsWith(routeStartsWith)) {
return;
}
beforeClear?.();
sessionStorage.removeItem(SessionStorageKey.SCROLL_POSITION);
});
return {
+11 -9
View File
@@ -3,10 +3,10 @@
* https://github.com/hperrin/svelte-material-ui/blob/master/packages/common/src/internal/useActions.ts
*/
export type SvelteActionReturnType<P> = {
export type SvelteActionReturnType<P> = void | {
update?: (newParams?: P) => void;
destroy?: () => void;
} | void;
};
export type SvelteHTMLActionType<P> = (node: HTMLElement, params?: P) => SvelteActionReturnType<P>;
@@ -46,13 +46,15 @@ export function useActions(node: HTMLElement | SVGElement, actions: ActionArray)
if (actions) {
for (const [i, returnEntry] of actionReturns.entries()) {
if (returnEntry && returnEntry.update) {
const actionEntry = actions[i];
if (Array.isArray(actionEntry) && actionEntry.length > 1) {
returnEntry.update(actionEntry[1]);
} else {
returnEntry.update();
}
if (!(returnEntry && returnEntry.update)) {
continue;
}
const actionEntry = actions[i];
if (Array.isArray(actionEntry) && actionEntry.length > 1) {
returnEntry.update(actionEntry[1]);
} else {
returnEntry.update();
}
}
}
@@ -16,7 +16,7 @@ export function dragAndDrop(options: DragAndDropOptions): Attachment {
const { index, onDragStart, onDragEnter, onDrop, onDragEnd, isDragging, isDragOver } = options;
const isFormElement = (el: HTMLElement) => {
return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT';
return ['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName);
};
const handleDragStart = (e: DragEvent) => {
+1 -1
View File
@@ -232,7 +232,7 @@ export const getSettingsProvider = ($t: MessageFormatter) => {
{
title: $t('my_immich_title'),
description: $t('my_immich_description'),
onAction: () => copyToClipboard(getMyImmichLink().toString()),
onAction: () => copyToClipboard(getMyImmichLink().href),
shortcuts: { ctrl: true, shift: true, key: 'm' },
},
];
@@ -18,7 +18,7 @@
}
const [group] = permission.split('.');
if (!permissions[group]) {
if (!Object.hasOwn(permissions, group)) {
permissions[group] = [];
}
permissions[group].push(permission);
@@ -13,10 +13,12 @@
const events: EventMap<Events> = {};
for (const [name, listener] of Object.entries(props)) {
if (listener) {
const event = name.slice(2) as keyof Events;
events[event] = listener as EventCallback<Events, typeof event>;
if (!listener) {
continue;
}
const event = name.slice(2) as keyof Events;
events[event] = listener as EventCallback<Events, typeof event>;
}
return assetViewerManager.on(events);
+7 -5
View File
@@ -19,12 +19,14 @@
let destroyed = false;
$effect(() => {
if (src !== undefined && capturedSource === undefined) {
capturedSource = src;
untrack(() => {
onStart?.();
});
if (src === undefined || capturedSource !== undefined) {
return;
}
capturedSource = src;
untrack(() => {
onStart?.();
});
});
onDestroy(() => {
+5 -3
View File
@@ -13,10 +13,12 @@
const events: EventMap<Events> = {};
for (const [name, listener] of Object.entries(props)) {
if (listener) {
const event = name.slice(2) as keyof Events;
events[event] = listener as EventCallback<Events, typeof event>;
if (!listener) {
continue;
}
const event = name.slice(2) as keyof Events;
events[event] = listener as EventCallback<Events, typeof event>;
}
return eventManager.on(events);
@@ -28,9 +28,9 @@
const label = $derived(schema.title ?? key);
const description = $derived(schema.description);
const getValue = <T,>(defaultValue?: T) => (root === true ? config : (config?.[key] ?? defaultValue)) as T;
const getValue = <T,>(defaultValue?: T) => (root ? config : (config?.[key] ?? defaultValue)) as T;
const setValue = <T,>(value: T) => {
if (root === true) {
if (root) {
config = value;
} else {
if (config === undefined) {
@@ -128,7 +128,7 @@
title={$t('admin.storage_template_enable_description')}
{disabled}
bind:checked={configToEdit.storageTemplate.enabled}
isEdited={!(configToEdit.storageTemplate.enabled === config.storageTemplate.enabled)}
isEdited={configToEdit.storageTemplate.enabled !== config.storageTemplate.enabled}
/>
{#if !minified}
@@ -137,9 +137,8 @@
{disabled}
subtitle={$t('admin.storage_template_hash_verification_enabled_description')}
bind:checked={configToEdit.storageTemplate.hashVerificationEnabled}
isEdited={!(
configToEdit.storageTemplate.hashVerificationEnabled === config.storageTemplate.hashVerificationEnabled
)}
isEdited={configToEdit.storageTemplate.hashVerificationEnabled !==
config.storageTemplate.hashVerificationEnabled}
/>
{/if}
@@ -233,7 +232,7 @@
required
inputType={SettingInputFieldType.TEXT}
bind:value={configToEdit.storageTemplate.template}
isEdited={!(configToEdit.storageTemplate.template === config.storageTemplate.template)}
isEdited={configToEdit.storageTemplate.template !== config.storageTemplate.template}
/>
<div class="flex-0">
@@ -30,10 +30,12 @@
});
$effect(() => {
if (!assetViewerManager.isViewing && returnToMap) {
returnToMap = false;
void onClick();
if (assetViewerManager.isViewing || !returnToMap) {
return;
}
returnToMap = false;
void onClick();
});
const loadMapMarkers = async () => {
@@ -38,10 +38,12 @@
let timelineManager = $state<TimelineManager>() as TimelineManager;
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {
handlePromiseError(fileUploadHandler({ files: value.files, albumId: album.id }));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
if (!(value.isDragging && value.files.length > 0)) {
return;
}
handlePromiseError(fileUploadHandler({ files: value.files, albumId: album.id }));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
});
const handleStartSlideshow = async () => {
@@ -81,11 +81,8 @@
// We make sure empty albums stay at the end of the list
if (a === unknownYear) {
return 1;
} else if (b === unknownYear) {
return -1;
} else {
return (Number.parseInt(a) - Number.parseInt(b)) * sortSign;
}
return b === unknownYear ? -1 : (Number.parseInt(a) - Number.parseInt(b)) * sortSign;
});
return sortedByYear.map(([year, albums]) => ({
@@ -106,13 +103,14 @@
// of the list
if (ownerIdA === currentUserId) {
return -sortSign;
} else if (ownerIdB === currentUserId) {
return sortSign;
} else {
const ownerA = albumsA[0].albumUsers[0].user;
const ownerB = albumsB[0].albumUsers[0].user;
return ownerA.name.localeCompare(ownerB.name, $locale) * sortSign;
}
if (ownerIdB === currentUserId) {
return sortSign;
}
const ownerA = albumsA[0].albumUsers[0].user;
const ownerB = albumsB[0].albumUsers[0].user;
return ownerA.name.localeCompare(ownerB.name, $locale) * sortSign;
});
return sortedByOwnerNames.map(([ownerId, albums]) => ({
@@ -96,7 +96,7 @@ describe('AlbumCard component', () => {
});
it('dispatches "onShowContextMenu" event on context menu click with mouse coordinates', async () => {
const contextMenuButton = sut.getByTestId('context-button-parent').children[0];
const contextMenuButton = sut.getByTestId('context-button-parent').firstElementChild!;
expect(contextMenuButton).toBeDefined();
// Mock getBoundingClientRect to return a bounding rectangle that will result in the expected position
@@ -101,7 +101,7 @@
e.preventDefault();
};
element.addEventListener('click', click);
element.addEventListener('pointerdown', start, true);
element.addEventListener('pointerdown', start, { capture: true });
element.addEventListener('pointerup', clearLongPressTimer, { capture: true, passive: true });
return {
destroy: () => {
@@ -173,10 +173,12 @@
});
const slideshowNavigationUnsubscribe = slideshowNavigation.subscribe((value) => {
if (value === SlideshowNavigation.Shuffle) {
slideshowHistory.reset();
slideshowHistory.queue(toTimelineAsset(asset));
if (value !== SlideshowNavigation.Shuffle) {
return;
}
slideshowHistory.reset();
slideshowHistory.queue(toTimelineAsset(asset));
});
return () => {
@@ -477,9 +479,7 @@
if (event.detail.direction === 'left') {
navigateAsset('next');
}
if (event.detail.direction === 'right') {
} else if (event.detail.direction === 'right') {
navigateAsset('previous');
}
};
@@ -191,9 +191,9 @@
</a>
</p>
{/if}
{#if (asset.exifInfo?.exifImageHeight && asset.exifInfo?.exifImageWidth) || asset.exifInfo?.fileSizeInByte}
{#if (asset.exifInfo?.exifImageHeight && asset.exifInfo.exifImageWidth) || asset.exifInfo?.fileSizeInByte}
<div class="flex gap-2 text-sm">
{#if asset.exifInfo?.exifImageHeight && asset.exifInfo?.exifImageWidth}
{#if asset.exifInfo?.exifImageHeight && asset.exifInfo.exifImageWidth}
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
<p>
{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)} MP
@@ -35,7 +35,8 @@
let formattedAge;
if (ageInYears < 0) {
return { formattedBirthDate: undefined, formattedAge: undefined, ...person };
} else if (ageInMonths < 12) {
}
if (ageInMonths < 12) {
formattedAge = $t('age_months', { values: { months: ageInMonths } });
} else if (ageInMonths > 12 && ageInMonths < 24) {
formattedAge = $t('age_year_months', { values: { months: ageInMonths - 12 } });
@@ -20,6 +20,7 @@
const handleSelectStart = (event: Event) => {
const target = event.currentTarget as HTMLElement;
requestAnimationFrame(() => {
// eslint-disable-next-line unicorn/no-unnecessary-global-this
const selection = globalThis.getSelection();
if (selection) {
selection.selectAllChildren(target);
@@ -115,6 +115,7 @@
// TODO move to action + command palette
const onCopyShortcut = (event: KeyboardEvent) => {
// eslint-disable-next-line unicorn/no-unnecessary-global-this
if (globalThis.getSelection()?.type === 'Range') {
return;
}
@@ -139,7 +140,7 @@
if (!url || !castManager.isCasting) {
return;
}
const fullUrl = new URL(url, globalThis.location.href);
const fullUrl = new URL(url, location.href);
try {
await castManager.loadMedia(fullUrl.href);
@@ -61,10 +61,12 @@
const hideControlsAfterDelay = () => {
timer = setTimeout(() => {
if (!isOverControls) {
showControls = false;
setCursorStyle('none');
if (isOverControls) {
return;
}
showControls = false;
setCursorStyle('none');
}, 2500);
};
@@ -77,10 +79,12 @@
});
unsubscribeStop = stopProgress.subscribe((value) => {
if (value) {
progressBar?.restart();
stopControlsHideTimer();
if (!value) {
return;
}
progressBar?.restart();
stopControlsHideTimer();
});
});
@@ -18,7 +18,7 @@
const description = $derived(asset.exifInfo?.description?.trim() || '');
const dateTime = $derived(
asset.exifInfo?.timeZone && asset.exifInfo?.dateTimeOriginal
asset.exifInfo?.timeZone && asset.exifInfo.dateTimeOriginal
? fromISODateTime(asset.exifInfo.dateTimeOriginal, asset.exifInfo.timeZone)
: fromISODateTimeUTC(asset.localDateTime),
);
@@ -152,13 +152,13 @@
},
useMediaCapabilities: false,
xhrSetup: (xhr: XMLHttpRequest, url: string) => {
const authenticatedUrl = new URL(url, globalThis.location.origin);
const authenticatedUrl = new URL(url, location.origin);
for (const [key, value] of Object.entries(authManager.params)) {
if (value) {
authenticatedUrl.searchParams.set(key, value as string);
}
}
xhr.open('GET', authenticatedUrl.toString());
xhr.open('GET', authenticatedUrl.href);
},
};
@@ -308,8 +308,7 @@
const onSwipe = (event: SwipeCustomEvent) => {
if (event.detail.direction === 'left') {
onNextAsset();
}
if (event.detail.direction === 'right') {
} else if (event.detail.direction === 'right') {
onPreviousAsset();
}
};
@@ -389,10 +388,12 @@
onended={onVideoEnded}
onseeking={onSeeking}
onplaying={(e: Event) => {
if (!hasFocused) {
(e.currentTarget as HTMLElement).focus();
hasFocused = true;
if (hasFocused) {
return;
}
(e.currentTarget as HTMLElement).focus();
hasFocused = true;
}}
onclose={onClose}
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })}
@@ -412,10 +413,12 @@
onended={onVideoEnded}
onseeking={onSeeking}
onplaying={(e) => {
if (!hasFocused) {
e.currentTarget.focus();
hasFocused = true;
if (hasFocused) {
return;
}
e.currentTarget.focus();
hasFocused = true;
}}
onclose={onClose}
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })}
@@ -51,7 +51,7 @@
if (!url || !castManager.isCasting) {
return;
}
const fullUrl = new URL(url, globalThis.location.href);
const fullUrl = new URL(url, location.href);
try {
await castManager.loadMedia(fullUrl.href, force);
@@ -63,7 +63,7 @@
};
function handleSeek(event: Event) {
const newTime = Number.parseFloat((event.target as HTMLInputElement).value);
const newTime = Number((event.target as HTMLInputElement).value);
castManager.seekTo(newTime);
}
</script>
@@ -37,9 +37,8 @@
if (isRotated) {
let [width, height] = ratio.value.split(':');
return `${height}:${width}`;
} else {
return ratio.value;
}
return ratio.value;
}
function ratioSelected(ratio: AspectRatioOption): boolean {
@@ -49,6 +49,6 @@ class ImmichTimeRange extends MediaTimeRange {
}
}
if (!globalThis.customElements.get('immich-time-range')) {
globalThis.customElements.define('immich-time-range', ImmichTimeRange);
if (!customElements.get('immich-time-range')) {
customElements.define('immich-time-range', ImmichTimeRange);
}
@@ -167,7 +167,7 @@
e.preventDefault();
};
element.addEventListener('click', click);
element.addEventListener('pointerdown', start, true);
element.addEventListener('pointerdown', start, { capture: true });
element.addEventListener('pointerup', clearLongPressTimer, { capture: true, passive: true });
return {
destroy: () => {
@@ -215,8 +215,7 @@
onkeydown={(evt) => {
if (evt.key === 'Enter') {
callClickHandlers();
}
if (evt.key === 'x') {
} else if (evt.key === 'x') {
onSelect?.(asset);
}
if (document.activeElement === element && evt.key === 'Escape') {
@@ -85,10 +85,7 @@
}}
ontimeupdate={({ currentTarget }) => {
const remaining = currentTarget.duration - currentTarget.currentTime;
remainingSeconds = Math.min(
Math.ceil(Number.isNaN(remaining) ? Number.POSITIVE_INFINITY : remaining),
durationInSeconds,
);
remainingSeconds = Math.min(Math.ceil(Number.isNaN(remaining) ? Infinity : remaining), durationInSeconds);
}}
></video>
{/if}
@@ -93,10 +93,10 @@
};
const handleReset = (id: string) => {
if (selectedPersonToReassign[id]) {
if (Object.hasOwn(selectedPersonToReassign, id)) {
delete selectedPersonToReassign[id];
}
if (selectedPersonToCreate[id]) {
if (Object.hasOwn(selectedPersonToCreate, id)) {
delete selectedPersonToCreate[id];
}
};
@@ -115,7 +115,7 @@
id: personId,
faceDto: { id: personWithFace.id },
});
} else if (selectedPersonToCreate[personWithFace.id]) {
} else if (Object.hasOwn(selectedPersonToCreate, personWithFace.id)) {
const data = await createPerson({ personCreateDto: {} });
peopleToCreate.push(data.id);
await reassignFacesById({
@@ -314,7 +314,7 @@
{/if}
</div>
{#if !selectedPersonToCreate[face.id]}
{#if !Object.hasOwn(selectedPersonToCreate, face.id)}
<p class="relative mt-1 truncate font-medium" title={personName}>
{#if selectedPersonToReassign[face.id]?.id}
{selectedPersonToReassign[face.id]?.name}
@@ -349,7 +349,7 @@
{/if}
</div>
<div class="absolute inset-e-8 top-[-3px] size-5 rounded-full">
{#if !selectedPersonToCreate[face.id] && !selectedPersonToReassign[face.id] && !face.person}
{#if !Object.hasOwn(selectedPersonToCreate, face.id) && !Object.hasOwn(selectedPersonToReassign, face.id) && !face.person}
<div
class="absolute inset-s-1/2 top-1/2 flex translate-[-50%] transform place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all"
>
@@ -71,7 +71,7 @@
}
// Sort by date descending (newest first), but put unknown date at the top
const sortedEntries = [...groups.entries()].sort((a, b) => {
const sortedEntries = [...groups].sort((a, b) => {
if (a[0] === unknownDateKey) {
return -1;
}
@@ -115,7 +115,7 @@
<hr />
{#each [...groupedBackups.entries()] as [dateGroup, groupBackups] (dateGroup)}
{#each [...groupedBackups] as [dateGroup, groupBackups] (dateGroup)}
<Stack gap={2}>
<div class="mt-5 mb-1">
<div class="flex w-max place-items-center gap-2 rounded-xl bg-primary-50 px-4 py-2">
@@ -21,7 +21,7 @@
let length = 13;
if (data) {
const valueLength = data.value.toString().length;
length = length - valueLength;
length -= valueLength;
}
return '0'.repeat(length);
@@ -35,10 +35,12 @@
let assets = $derived(sharedLink.assets);
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {
handlePromiseError(handleUploadAssets(value.files));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
if (!(value.isDragging && value.files.length > 0)) {
return;
}
handlePromiseError(handleUploadAssets(value.files));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
});
const downloadAssets = async () => {
@@ -345,10 +345,12 @@
{
shortcut: { key: 'Escape' },
onShortcut: (event) => {
if (isOpen) {
event.stopPropagation();
closeDropdown();
if (!isOpen) {
return;
}
event.stopPropagation();
closeDropdown();
},
},
]}
@@ -399,7 +401,7 @@
aria-selected={selectedIndex === 0}
aria-disabled={true}
class="w-full cursor-default px-4 py-2 text-start hover:bg-gray-200 aria-selected:bg-gray-200 dark:hover:bg-gray-700 aria-selected:dark:bg-gray-700"
id={`${listboxId}-${0}`}
id={`${listboxId}-0`}
onclick={closeDropdown}
>
{allowCreate ? searchQuery : $t('no_results')}
@@ -18,7 +18,7 @@
};
};
const parsePixels = (style: string) => Number.parseInt(style, 10) || 0;
const parsePixels = (style: string) => Math.trunc(Number(style)) || 0;
const getItemCount = (container: HTMLElement, containerWidth: number) => {
if (!container.firstElementChild) {
@@ -32,6 +32,7 @@
let isActive = $derived($selectedIdStore === id);
const handleClick = () => {
// eslint-disable-next-line unicorn/no-optional-chaining-on-undeclared-variable
$optionClickCallbackStore?.();
onClick();
};
@@ -57,11 +57,13 @@
onClose?.();
};
$effect(() => {
if (isOpen && menuContainer) {
triggerElement = document.activeElement as HTMLElement;
menuContainer.focus();
$optionClickCallbackStore = closeContextMenu;
if (!(isOpen && menuContainer)) {
return;
}
triggerElement = document.activeElement as HTMLElement;
menuContainer.focus();
$optionClickCallbackStore = closeContextMenu;
});
const oncontextmenu = async (event: MouseEvent) => {
@@ -109,12 +109,14 @@
let lastEndReachedHeight = 0;
$effect(() => {
if (geometry.containerHeight - slidingWindow.bottom <= viewport.height) {
const contentHeight = geometry.containerHeight;
if (lastEndReachedHeight !== contentHeight) {
debouncedOnEndReached();
lastEndReachedHeight = contentHeight;
}
if (geometry.containerHeight - slidingWindow.bottom > viewport.height) {
return;
}
const contentHeight = geometry.containerHeight;
if (lastEndReachedHeight !== contentHeight) {
debouncedOnEndReached();
lastEndReachedHeight = contentHeight;
}
});
@@ -109,14 +109,16 @@
);
export function addClipMapMarker(lng: number, lat: number) {
if (map) {
if (marker) {
marker.remove();
}
center = { lng, lat };
marker = new Marker().setLngLat([lng, lat]).addTo(map);
if (!map) {
return;
}
if (marker) {
marker.remove();
}
center = { lng, lat };
marker = new Marker().setLngLat([lng, lat]).addTo(map);
}
function handleAssetClick(assetId: string, map: Map | null) {
@@ -159,17 +161,19 @@
}
function handleMapClick(event: MapMouseEvent) {
if (clickable) {
const { lng, lat } = event.lngLat;
onClickPoint({ lng, lat });
if (!clickable) {
return;
}
if (marker) {
marker.remove();
}
const { lng, lat } = event.lngLat;
onClickPoint({ lng, lat });
if (map) {
marker = new Marker().setLngLat([lng, lat]).addTo(map);
}
if (marker) {
marker.remove();
}
if (map) {
marker = new Marker().setLngLat([lng, lat]).addTo(map);
}
}
@@ -254,13 +258,16 @@
};
afterNavigate(() => {
if (map) {
map.resize();
if (!map) {
return;
}
if (globalThis.location.hash) {
const hashChangeEvent = new HashChangeEvent('hashchange');
globalThis.dispatchEvent(hashChangeEvent);
}
map.resize();
if (location.hash) {
const hashChangeEvent = new HashChangeEvent('hashchange');
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.dispatchEvent(hashChangeEvent);
}
});
@@ -156,10 +156,12 @@
};
const onEnter = (event: KeyboardEvent) => {
if (selectedId) {
event.preventDefault();
searchHistoryBox?.selectActiveOption();
if (!selectedId) {
return;
}
event.preventDefault();
searchHistoryBox?.selectActiveOption();
};
const onInput = () => {
@@ -44,7 +44,8 @@
export function moveSelection(increment: 1 | -1) {
if (!isSearchSuggestions) {
return;
} else if (selectedIndex === undefined) {
}
if (selectedIndex === undefined) {
selectedIndex = increment === 1 ? 0 : suggestionCount - 1;
} else if (selectedIndex + increment < 0 || selectedIndex + increment >= suggestionCount) {
clearSelection();
@@ -18,11 +18,13 @@
const defaultLangOption = { label: defaultLang.name, value: defaultLang.code };
const handleLanguageChange = async (newLang: string | undefined) => {
if (newLang) {
$lang = newLang;
await i18nLocale.set(convertBCP47(newLang));
await invalidateAll();
if (!newLang) {
return;
}
$lang = newLang;
await i18nLocale.set(convertBCP47(newLang));
await invalidateAll();
};
let closestLanguage = $derived(getClosestAvailableLocale([$lang], langCodes));
@@ -11,7 +11,7 @@
const refreshAlbums = async () => {
try {
const allAlbums = await getAllAlbums({});
albums = allAlbums.sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)).slice(0, 3);
albums = allAlbums.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()).slice(0, 3);
userInteraction.recentAlbums = albums;
} catch (error) {
handleError(error, $t('failed_to_load_assets'));
+14 -13
View File
@@ -99,13 +99,15 @@
) => {
if (scrubberMonth === 'lead-in') {
return relativeTopOffset * scrubberMonthPercent;
} else if (scrubberMonth === 'lead-out') {
}
if (scrubberMonth === 'lead-out') {
let offset = relativeTopOffset;
for (const segment of segments) {
offset += segment.height;
}
return offset + relativeBottomOffset * scrubberMonthPercent;
} else if (scrubberMonth) {
}
if (scrubberMonth) {
let offset = relativeTopOffset;
let match = false;
for (const segment of segments) {
@@ -120,9 +122,8 @@
offset += scrubberMonthPercent * relativeBottomOffset;
}
return offset;
} else {
return scrubOverallPercent * (height - (PADDING_TOP + PADDING_BOTTOM));
}
return scrubOverallPercent * (height - (PADDING_TOP + PADDING_BOTTOM));
};
const scrollY = $derived(
toScrollFromTimelineMonthPercentage(viewportTopMonth, viewportTopMonthScrollPercent, timelineScrollPercent),
@@ -229,14 +230,14 @@
if (scrollY !== undefined) {
if (scrollY < relativeTopOffset) {
return segments.at(0)?.dateFormatted;
} else {
let offset = relativeTopOffset;
for (const segment of segments) {
offset += segment.height;
}
if (scrollY > offset) {
return segments.at(-1)?.dateFormatted;
}
}
let offset = relativeTopOffset;
for (const segment of segments) {
offset += segment.height;
}
if (scrollY > offset) {
return segments.at(-1)?.dateFormatted;
}
}
return scrollSegment?.dateFormatted || '';
@@ -338,7 +339,7 @@
overallScrollPercent: toTimelineY(hoverY),
scrubberMonthScrollPercent: timelineMonthPercentY,
};
if (wasDragging === false && isDragging) {
if (!wasDragging && isDragging) {
void startScrub?.(scrubData);
void onScrub?.(scrubData);
}
@@ -201,7 +201,7 @@
export const scrollAfterNavigate = async () => {
if (timelineManager.viewportHeight === 0 || timelineManager.viewportWidth === 0) {
// this can happen if you do the following navigation order
// /photos?at=<id>, /photos/<id>, http://example.com, browser back, browser back
// /photos?at=<id>, /photos/<id>, https://example.com, browser back, browser back
const rect = scrollableElement?.getBoundingClientRect();
if (rect) {
timelineManager.viewportHeight = rect.height;
@@ -209,10 +209,7 @@
}
}
const scrollTarget = assetViewerManager.gridScrollTarget?.at;
let scrolled = false;
if (scrollTarget) {
scrolled = await scrollAndLoadAsset(scrollTarget);
}
const scrolled = scrollTarget ? await scrollAndLoadAsset(scrollTarget) : false;
if (!scrolled) {
// if the asset is not found, scroll to the top
timelineManager.scrollTo(0);
@@ -503,10 +500,12 @@
});
$effect(() => {
if (assetViewerManager.asset && assetViewerManager.isViewing) {
const { localDateTime } = getTimes(assetViewerManager.asset.fileCreatedAt, DateTime.local().offset / 60);
void timelineManager.loadTimelineMonth({ year: localDateTime.year, month: localDateTime.month });
if (!(assetViewerManager.asset && assetViewerManager.isViewing)) {
return;
}
const { localDateTime } = getTimes(assetViewerManager.asset.fileCreatedAt, DateTime.local().offset / 60);
void timelineManager.loadTimelineMonth({ year: localDateTime.year, month: localDateTime.month });
});
const assetSelectHandler = (
@@ -582,10 +581,7 @@
bind:scrubberWidth
onScrubKeyDown={(evt) => {
evt.preventDefault();
let amount = 50;
if (keyboardManager.shift) {
amount = 500;
}
let amount = keyboardManager.shift ? 500 : 50;
if (evt.key === 'ArrowUp') {
amount = -amount;
if (keyboardManager.shift) {
+5 -3
View File
@@ -41,10 +41,12 @@
for (const option of Object.values(element.options)) {
for (const pluralElement of option.value) {
if (pluralElement.type === TYPE.tag) {
const tag = pluralElement.value;
replacements[tag] = (...parts) => `<${tag}>${parts}</${tag}>`;
if (pluralElement.type !== TYPE.tag) {
continue;
}
const tag = pluralElement.value;
replacements[tag] = (...parts) => `<${tag}>${parts}</${tag}>`;
}
}
@@ -134,10 +134,12 @@ class AssetViewerManager extends BaseEventManager<Events> {
}
cancelZoomAnimation() {
if (this.#animationFrameId !== null) {
cancelAnimationFrame(this.#animationFrameId);
this.#animationFrameId = null;
if (this.#animationFrameId === null) {
return;
}
cancelAnimationFrame(this.#animationFrameId);
this.#animationFrameId = null;
}
animatedZoom(targetZoom: number, duration = 300) {
+1 -1
View File
@@ -109,7 +109,7 @@ class AuthManager {
await goto(redirectUri);
} else {
globalThis.location.href = redirectUri;
location.assign(redirectUri);
}
}
@@ -16,7 +16,7 @@ class DownloadManager {
return;
}
if (!this.assets[key]) {
if (!Object.hasOwn(this.assets, key)) {
this.assets[key] = { progress: 0, total: 0, percentage: 0, abort: null };
}
@@ -193,6 +193,7 @@ class TransformManager implements EditToolManager {
passive: true,
});
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.addEventListener('mousemove', (e: MouseEvent) => transformManager.handleMouseMove(e), { passive: true });
const transformEdits = edits.filter((e) => e.action === 'rotate' || e.action === 'mirror');
@@ -210,6 +211,7 @@ class TransformManager implements EditToolManager {
}
onDeactivate() {
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.removeEventListener('mousemove', transformManager.handleMouseMove);
this.reset();
@@ -553,6 +555,7 @@ class TransformManager implements EditToolManager {
}
document.body.style.userSelect = 'none';
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.addEventListener('mouseup', () => this.handleMouseUp(), { passive: true });
}
@@ -571,6 +574,7 @@ class TransformManager implements EditToolManager {
}
handleMouseUp() {
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.removeEventListener('mouseup', this.handleMouseUp);
document.body.style.userSelect = '';
@@ -13,10 +13,11 @@ class LanguageManager {
rtl = $state(false);
init() {
if (!this.initialized) {
this.initialized = true;
lang.subscribe((lang) => this.setLanguage(lang));
if (this.initialized) {
return;
}
this.initialized = true;
lang.subscribe((lang) => this.setLanguage(lang));
}
setLanguage(code: string) {
@@ -89,4 +89,5 @@ class MediaCapabilitiesManager {
}
export const mediaCapabilitiesManager = new MediaCapabilitiesManager();
// eslint-disable-next-line unicorn/no-top-level-side-effects
mediaCapabilitiesManager.init();
@@ -19,10 +19,10 @@ export class GroupInsertionCache {
}
setTimelineDay(timelineDay: TimelineDay, { year, month, day }: TimelineDate) {
if (!this.#lookupCache[year]) {
if (!Object.hasOwn(this.#lookupCache, year)) {
this.#lookupCache[year] = {};
}
if (!this.#lookupCache[year][month]) {
if (!Object.hasOwn(this.#lookupCache[year], month)) {
this.#lookupCache[year][month] = {};
}
this.#lookupCache[year][month][day] = timelineDay;
@@ -41,8 +41,6 @@ export function layoutTimelineMonth(timelineManager: TimelineManager, month: Tim
timelineDay.col = timelineDayCol++;
timelineDay.start = cumulativeWidth;
timelineDay.top = cumulativeHeight;
cumulativeWidth += timelineDay.width + timelineManager.gap;
} else {
// Move to next row
cumulativeHeight += currentRowHeight;
@@ -57,8 +55,8 @@ export function layoutTimelineMonth(timelineManager: TimelineManager, month: Tim
timelineDay.top = cumulativeHeight;
timelineDayCol++;
cumulativeWidth += timelineDay.width + timelineManager.gap;
}
cumulativeWidth += timelineDay.width + timelineManager.gap;
currentRowHeight = timelineDay.height + timelineManager.headerHeight;
}
@@ -13,7 +13,7 @@ export function updateObject(target: any, source: any): boolean {
}
const isDate = target[key] instanceof Date;
if (typeof target[key] === 'object' && !isDate) {
updated = updated || updateObject(target[key], source[key]);
updated ||= updateObject(target[key], source[key]);
} else {
if (target[key] !== source[key]) {
target[key] = source[key];
@@ -357,10 +357,7 @@ export class TimelineManager extends VirtualScrollManager {
}
async loadTimelineMonth(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise<void> {
let cancelable = true;
if (options) {
cancelable = options.cancelable;
}
const cancelable = options?.cancelable ?? true;
const timelineMonth = getTimelineMonthByDate(this, yearMonth);
if (!timelineMonth) {
return;
@@ -517,10 +514,7 @@ export class TimelineManager extends VirtualScrollManager {
// eslint-disable-next-line svelte/prefer-svelte-reactivity
const idsToUpdate = new Set(cache.keys());
const result = this.#runAssetCallback(idsToUpdate, (asset) => void updateObject(asset, cache.get(asset.id)));
const notUpdated: TimelineAsset[] = [];
for (const assetId of result.notUpdated) {
notUpdated.push(cache.get(assetId)!);
}
const notUpdated: TimelineAsset[] = Array.from(result.notUpdated, (assetId) => cache.get(assetId)!);
return notUpdated;
}
@@ -145,21 +145,23 @@ export class TimelineMonth {
const combinedMoveAssets: MoveAsset[][] = [];
let index = timelineDays.length;
while (index--) {
if (idsToProcess.size > 0) {
const group = timelineDays[index];
const { moveAssets, processedIds, changedGeometry } = group.runAssetCallback(ids, callback);
if (moveAssets.length > 0) {
combinedMoveAssets.push(moveAssets);
}
idsToProcess = setDifference(idsToProcess, processedIds);
for (const id of processedIds) {
idsProcessed.add(id);
}
combinedChangedGeometry = combinedChangedGeometry || changedGeometry;
if (group.viewerAssets.length === 0) {
timelineDays.splice(index, 1);
combinedChangedGeometry = true;
}
if (idsToProcess.size === 0) {
continue;
}
const group = timelineDays[index];
const { moveAssets, processedIds, changedGeometry } = group.runAssetCallback(ids, callback);
if (moveAssets.length > 0) {
combinedMoveAssets.push(moveAssets);
}
idsToProcess = setDifference(idsToProcess, processedIds);
for (const id of processedIds) {
idsProcessed.add(id);
}
combinedChangedGeometry ||= changedGeometry;
if (group.viewerAssets.length === 0) {
timelineDays.splice(index, 1);
combinedChangedGeometry = true;
}
}
return {
@@ -195,7 +197,7 @@ export class TimelineMonth {
ownerId: bucketAssets.ownerId[i],
projectionType: bucketAssets.projectionType[i],
ratio: bucketAssets.ratio[i],
stack: bucketAssets.stack?.[i]
stack: bucketAssets.stack?.at(i)
? {
id: bucketAssets.stack[i]![0],
primaryAssetId: bucketAssets.id[i],
@@ -206,7 +208,7 @@ export class TimelineMonth {
people: null, // People are not included in the bucket assets
};
if (bucketAssets.latitude?.[i] && bucketAssets.longitude?.[i]) {
if (bucketAssets.latitude?.at(i) && bucketAssets.longitude?.at(i)) {
timelineAsset.latitude = bucketAssets.latitude?.[i];
timelineAsset.longitude = bucketAssets.longitude?.[i];
}
@@ -137,6 +137,7 @@
break;
}
case 'Control': {
// eslint-disable-next-line unicorn/no-late-event-control
e.preventDefault();
handleMultiSelect();
break;
@@ -85,8 +85,8 @@
// Try to parse coordinate pair from search input in the format `LATITUDE, LONGITUDE` as floats
const coordinateParts = searchWord.split(',').map((part) => part.trim());
if (coordinateParts.length === 2) {
const coordinateLat = Number.parseFloat(coordinateParts[0]);
const coordinateLng = Number.parseFloat(coordinateParts[1]);
const coordinateLat = Number(coordinateParts[0]);
const coordinateLng = Number(coordinateParts[1]);
if (
!Number.isNaN(coordinateLat) &&
@@ -106,18 +106,22 @@
searchPlaces({ name: searchWord })
.then((searchResult) => {
// skip result when a newer search is happening
if (latestSearchTimeout === searchTimeout) {
places = searchResult;
showLoadingSpinner = false;
if (latestSearchTimeout !== searchTimeout) {
return;
}
places = searchResult;
showLoadingSpinner = false;
})
.catch((error) => {
// skip error when a newer search is happening
if (latestSearchTimeout === searchTimeout) {
places = [];
handleError(error, $t('errors.cant_search_places'));
showLoadingSpinner = false;
if (latestSearchTimeout !== searchTimeout) {
return;
}
places = [];
handleError(error, $t('errors.cant_search_places'));
showLoadingSpinner = false;
});
}, timeDebounceOnSearch);
latestSearchTimeout = searchTimeout;
@@ -28,6 +28,7 @@
const changePersonToMerge = (newPerson: PersonResponseDto) => {
const index = potentialMergePeople.indexOf(newPerson);
// eslint-disable-next-line unicorn/no-unreadable-array-destructuring
[potentialMergePeople[index], personToBeMergedInto] = [personToBeMergedInto, potentialMergePeople[index]];
choosePersonToMerge = false;
};
+2 -4
View File
@@ -45,10 +45,8 @@
}
const asFilter = (searchQuery: SmartSearchDto | MetadataSearchDto): SearchFilter => {
let query = '';
if ('query' in searchQuery && searchQuery.query) {
query = searchQuery.query;
}
let query = 'query' in searchQuery && searchQuery.query ? searchQuery.query : '';
if ('originalFileName' in searchQuery && searchQuery.originalFileName) {
query = searchQuery.originalFileName;
}
+1 -1
View File
@@ -61,7 +61,7 @@
<ServerAboutItem id="build" title={$t('build')} version={info.build} versionHref={info.buildUrl} />
{/if}
{#if info.buildImage && info.buildImage}
{#if info.buildImage && info.buildImageUrl}
<ServerAboutItem
id="build-image"
title={$t('build_image')}
+1 -1
View File
@@ -37,7 +37,7 @@ export type ZoneOption = {
valid: boolean;
};
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const userTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
const knownTimezones = Intl.supportedValuesOf('timeZone');
export function getTimezones(selectedDate: string) {
+1
View File
@@ -65,6 +65,7 @@ describe('Route', () => {
describe(Route.continue.name, () => {
beforeEach(() => {
// @ts-expect-error - override location for testing
// eslint-disable-next-line unicorn/no-global-object-property-assignment
globalThis.location = new URL('https://my.immich.server');
vi.spyOn(document, 'baseURI', 'get').mockReturnValue('https://my.immich.server/');
});
+1 -5
View File
@@ -31,11 +31,7 @@ const asQueryString = (
return false;
}
if (skipEmptyStrings && value === '') {
return false;
}
return true;
return !(skipEmptyStrings && value === '');
})
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
@@ -91,7 +91,7 @@ export const handleDeleteDatabaseBackup = async (...filenames: string[]) => {
};
export const handleDownloadDatabaseBackup = (filename: string) => {
location.href = getBaseUrl() + '/admin/database-backups/' + filename;
location.assign(getBaseUrl() + '/admin/database-backups/' + filename);
};
export const handleUploadDatabaseBackup = async () => {
+1 -1
View File
@@ -61,7 +61,7 @@ export const getSharedLinkActions = ($t: MessageFormatter, sharedLink: SharedLin
export const asUrl = (sharedLink: SharedLinkResponseDto) => {
const path = Route.viewSharedLink(sharedLink);
return new URL(path, serverConfigManager.value.externalDomain || globalThis.location.origin).href;
return new URL(path, serverConfigManager.value.externalDomain || location.origin).href;
};
export const handleCreateSharedLink = async (dto: SharedLinkCreateDto) => {
@@ -64,7 +64,7 @@ export const handleSystemConfigSave = async (update: Partial<SystemConfigDto>) =
};
export const handleUploadConfig = () => {
const input = globalThis.document.createElement('input');
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', '.json');
input.setAttribute('style', 'display: none');
@@ -83,6 +83,6 @@ export const handleUploadConfig = () => {
.catch((error) => console.error('Error handling JSON config upload', error))
.finally(() => input.remove());
});
globalThis.document.body.append(input);
document.body.append(input);
input.click();
};
+2 -2
View File
@@ -159,11 +159,11 @@ export const handleNavigateUserAdmin = async (user: UserAdminResponseDto) => {
const generatePassword = (length: number = 16) => {
let generatedPassword = '';
const characterSet = '0123456789' + 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + ',.-{}+!#$%/()=?';
const characterSet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.-{}+!#$%/()=?';
for (let i = 0; i < length; i++) {
let randomNumber = crypto.getRandomValues(new Uint32Array(1))[0];
randomNumber = randomNumber / 2 ** 32;
randomNumber /= 2 ** 32;
randomNumber = Math.floor(randomNumber * characterSet.length);
generatedPassword += characterSet[randomNumber];
-5
View File
@@ -3,8 +3,6 @@ import {
deleteWorkflow,
updateWorkflow,
WorkflowTrigger,
type AlbumResponseDto,
type PersonResponseDto,
type WorkflowCreateDto,
type WorkflowResponseDto,
type WorkflowUpdateDto,
@@ -32,9 +30,6 @@ import { copyToClipboard, downloadJson } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { getFormatter } from '$lib/utils/i18n';
export type PickerSubType = 'album-picker' | 'people-picker';
export type PickerMetadata = AlbumResponseDto | PersonResponseDto | AlbumResponseDto[] | PersonResponseDto[];
export const getWorkflowsActions = ($t: MessageFormatter) => {
const Create: ActionItem = {
title: $t('create_workflow'),
@@ -8,8 +8,11 @@ class KeyboardManager {
if (globalThis.window === undefined) {
return;
}
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.addEventListener('keydown', this.#update);
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.addEventListener('keyup', this.#update);
// eslint-disable-next-line unicorn/no-unnecessary-global-this
globalThis.addEventListener('blur', this.#clear);
}
+10 -6
View File
@@ -58,10 +58,12 @@ function createSlideshowStore() {
set: (value: boolean) => {
// Trigger an action whenever the restartProgress is set to true. Automatically
// reset the restart state after that
if (value) {
restartState.set(true);
restartState.set(false);
if (!value) {
return;
}
restartState.set(true);
restartState.set(false);
},
},
stopProgress: {
@@ -69,10 +71,12 @@ function createSlideshowStore() {
set: (value: boolean) => {
// Trigger an action whenever the stopProgress is set to true. Automatically
// reset the stop state after that
if (value) {
stopState.set(true);
stopState.set(false);
if (!value) {
return;
}
stopState.set(true);
stopState.set(false);
},
},
slideshowNavigation,
+1 -1
View File
@@ -23,7 +23,7 @@ function createUploadStore() {
const addItem = (newAsset: UploadAsset) => {
uploadAssets.update(($assets) => {
const duplicate = $assets.find((asset) => asset.id === newAsset.id);
const duplicate = $assets.some((asset) => asset.id === newAsset.id);
if (duplicate) {
return $assets.map((asset) => (asset.id === newAsset.id ? newAsset : asset));
}
+1
View File
@@ -30,6 +30,7 @@ const reset = () => {
Object.assign(userInteraction, defaultUserInteraction);
};
// eslint-disable-next-line unicorn/no-top-level-side-effects
eventManager.on({
AlbumCreate: () => resetRecentAlbums(),
AlbumUpdate: () => resetRecentAlbums(),
+8 -5
View File
@@ -60,6 +60,7 @@ export const websocketStore = {
export const websocketEvents = createEventEmitter(websocket);
// eslint-disable-next-line unicorn/no-top-level-side-effects
websocket
.on('connect', () => {
eventManager.emit('WebsocketConnect');
@@ -113,16 +114,18 @@ export const waitForWebsocketEvent = <T extends keyof Events>(
return new Promise((resolve, reject) => {
// @ts-expect-error: The typings are weird on this?
const cleanup = websocketEvents.on(event, (...args: Parameters<Events[T]>) => {
if (!predicate || predicate(...args)) {
cleanup();
clearTimeout(timer);
resolve(args);
if (predicate && !predicate(...args)) {
return;
}
cleanup();
clearTimeout(timer);
resolve(args);
});
const timer = setTimeout(() => {
cleanup();
reject(new Error(`Timeout waiting for event: ${String(event)}`));
reject(new Error(`Timeout waiting for event: ${event}`));
}, timeout);
});
};
+5 -5
View File
@@ -109,11 +109,10 @@ export const uploadRequest = async <T>(options: UploadRequestOptions): Promise<{
});
xhr.addEventListener('load', () => {
unsubscribe();
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
unsubscribe();
resolve({ data: xhr.response as T, status: xhr.status });
} else {
unsubscribe();
reject(new ApiError(xhr.statusText, xhr.status, xhr.response));
}
});
@@ -326,9 +325,9 @@ export const oauth = {
authorize: async (location: Location) => {
const $t = get(t);
try {
const redirectUri = location.href.split('?')[0];
const redirectUri = location.href.split('?', 1)[0];
const { url } = await startOAuth({ oAuthConfigDto: { redirectUri } });
globalThis.location.href = url;
globalThis.location.assign(url);
return true;
} catch (error) {
handleError(error, $t('errors.unable_to_login_with_oauth'));
@@ -430,7 +429,8 @@ export const isEnabled = ({ $if }: IfLike) => $if?.() ?? true;
export const transformToTitleCase = (text: string) => {
if (text.length === 0) {
return text;
} else if (text.length === 1) {
}
if (text.length === 1) {
return text.charAt(0).toUpperCase();
}
+14 -12
View File
@@ -68,19 +68,21 @@ const undoDeleteAssets = async (onUndoDelete: OnUndoDelete, assets: TimelineAsse
* @param {StackResponse} stackResponse - The stack response containing the stack and assets to delete.
*/
export function updateStackedAssetInTimeline(timelineManager: TimelineManager, { stack, toDeleteIds }: StackResponse) {
if (stack != undefined) {
timelineManager.update(
[stack.primaryAssetId],
(asset) =>
(asset.stack = {
id: stack.id,
primaryAssetId: stack.primaryAssetId,
assetCount: stack.assets.length,
}),
);
timelineManager.removeAssets(toDeleteIds);
if (stack == undefined) {
return;
}
timelineManager.update(
[stack.primaryAssetId],
(asset) =>
(asset.stack = {
id: stack.id,
primaryAssetId: stack.primaryAssetId,
assetCount: stack.assets.length,
}),
);
timelineManager.removeAssets(toDeleteIds);
}
/**
@@ -86,6 +86,7 @@ export class AdaptiveImageLoader {
const config = this.qualityConfigs[quality];
// eslint-disable-next-line unicorn/no-computed-property-existence-check
if (!this.status.urls[quality]) {
return;
}
@@ -129,6 +130,7 @@ export class AdaptiveImageLoader {
return false;
}
// eslint-disable-next-line unicorn/no-computed-property-existence-check
if (this.status.urls[quality]) {
return true;
}
+9 -8
View File
@@ -92,13 +92,13 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn
for (let index = 0; index < downloadInfo.archives.length; index++) {
const archive = downloadInfo.archives[index];
const suffix = downloadInfo.archives.length > 1 ? `+${index + 1}` : '';
const archiveName = fileName.replace('.zip', `${suffix}-${DateTime.now().toFormat('yyyyLLdd_HHmmss')}.zip`);
const archiveName = fileName.replace('.zip', () => `${suffix}-${DateTime.now().toFormat('yyyyLLdd_HHmmss')}.zip`);
const queryParams = asQueryString(authManager.params);
let downloadKey = `${archiveName} `;
if (downloadInfo.archives.length > 1) {
downloadKey = `${archiveName} (${index + 1}/${downloadInfo.archives.length})`;
}
const downloadKey =
downloadInfo.archives.length > 1
? `${archiveName} (${index + 1}/${downloadInfo.archives.length})`
: `${archiveName} `;
const abort = new AbortController();
downloadManager.add(downloadKey, archive.size, abort);
@@ -131,7 +131,7 @@ export const downloadArchive = async (fileName: string, options: Omit<DownloadIn
*/
export function getFilenameExtension(filename: string): string {
const lastIndex = Math.max(0, filename.lastIndexOf('.'));
const startIndex = (lastIndex || Number.POSITIVE_INFINITY) + 1;
const startIndex = (lastIndex || Infinity) + 1;
return filename.slice(startIndex).toLowerCase();
}
@@ -144,11 +144,11 @@ export function getAssetFilename(asset: AssetResponseDto): string {
}
function isRotated90CW(orientation: number) {
return orientation === 5 || orientation === 6 || orientation === 90;
return [5, 6, 90].includes(orientation);
}
function isRotated270CW(orientation: number) {
return orientation === 7 || orientation === 8 || orientation === -90;
return [7, 8, -90].includes(orientation);
}
export function isFlipped(orientation?: string | null) {
@@ -245,6 +245,7 @@ async function addSupportedMimeTypes(): Promise<void> {
heicImg.src =
'data:image/heic;base64,AAAAGGZ0eXBoZWljAAAAAG1pZjFoZWljAAABrW1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAHBpY3QAAAAAAAAAAAAAAAAAAAAADnBpdG0AAAAAAAIAAAAQaWRhdAAAAAAAAQABAAAAOGlsb2MBAAAAREAAAgABAAAAAAAAAc0AAQAAAAAAAAAsAAIAAQAAAAAAAAABAAAAAAAAAAgAAAA4aWluZgAAAAAAAgAAABVpbmZlAgAAAQABAABodmMxAAAAABVpbmZlAgAAAAACAABncmlkAAAAANhpcHJwAAAAtmlwY28AAAB2aHZjQwEDcAAAAAAAAAAAAB7wAPz9+PgAAA8DIAABABhAAQwB//8DcAAAAwCQAAADAAADAB66AkAhAAEAKkIBAQNwAAADAJAAAAMAAAMAHqAggQWW6q6a5uBAQMCAAAADAIAAAAMAhCIAAQAGRAHBc8GJAAAAFGlzcGUAAAAAAAAAAQAAAAEAAAAUaXNwZQAAAAAAAABAAAAAQAAAABBwaXhpAAAAAAMICAgAAAAaaXBtYQAAAAAAAAACAAECgQMAAgIChAAAABppcmVmAAAAAAAAAA5kaW1nAAIAAQABAAAANG1kYXQAAAAoKAGvCchMZYA50NoPIfzz81Qfsm577GJt3lf8kLAr+NbNIoeRR7JeYA=='; // Small valid HEIC/HEIF image
}
// eslint-disable-next-line unicorn/no-top-level-side-effects
void addSupportedMimeTypes();
/**
+5 -3
View File
@@ -28,10 +28,12 @@ export const authenticate = async (url: URL, options?: AuthOptions) => {
};
export const requestServerInfo = async () => {
if (authManager.authenticated) {
const data = await getStorage();
userInteraction.serverInfo = data;
if (!authManager.authenticated) {
return;
}
const data = await getStorage();
userInteraction.serverInfo = data;
};
export const getAccountAge = (): number => {
+1 -1
View File
@@ -23,7 +23,7 @@ const byteUnits = [ByteUnit.B, ByteUnit.KiB, ByteUnit.MiB, ByteUnit.GiB, ByteUni
export function getBytesWithUnit(bytes: number, maxPrecision = 1): [number, ByteUnit] {
const magnitude = Math.floor(Math.log(bytes === 0 ? 1 : bytes) / Math.log(1024));
return [Number.parseFloat((bytes / 1024 ** magnitude).toFixed(maxPrecision)), byteUnits[magnitude]];
return [Number((bytes / 1024 ** magnitude).toFixed(maxPrecision)), byteUnits[magnitude]];
}
/**
@@ -41,11 +41,12 @@ export class GCastDestination implements ICastDestination {
return;
}
// eslint-disable-next-line unicorn/no-global-object-property-assignment
window['__onGCastApiAvailable'] = (isAvailable: boolean) => {
resolve(isAvailable);
};
if (!document.querySelector(`script[src="${FRAMEWORK_LINK}"]`)) {
if (!document.querySelector(`script[src="${CSS.escape(FRAMEWORK_LINK)}"]`)) {
const script = document.createElement('script');
script.src = FRAMEWORK_LINK;
document.body.append(script);
+13 -13
View File
@@ -27,23 +27,23 @@ export const getShortDateRange = (startTimestamp: string, endTimestamp: string)
// Same year and month.
// e.g.: aug. 2024
return endDateLocalized;
} else {
// Same year but different month.
// e.g.: jul. - sept. 2024
const startMonthLocalized = startDate.toLocaleString({
month: 'short',
});
return `${startMonthLocalized} - ${endDateLocalized}`;
}
} else {
// Different year.
// e.g.: feb. 2021 - sept. 2024
const startDateLocalized = startDate.toLocaleString({
// Same year but different month.
// e.g.: jul. - sept. 2024
const startMonthLocalized = startDate.toLocaleString({
month: 'short',
year: 'numeric',
});
return `${startDateLocalized} - ${endDateLocalized}`;
return `${startMonthLocalized} - ${endDateLocalized}`;
}
// Different year.
// e.g.: feb. 2021 - sept. 2024
const startDateLocalized = startDate.toLocaleString({
month: 'short',
year: 'numeric',
});
return `${startDateLocalized} - ${endDateLocalized}`;
};
const formatDate = (date?: string) => {
+3 -3
View File
@@ -209,7 +209,7 @@ const metadataFields = [
icon: mdiPhoneRotateLandscape,
titleKey: 'orientation',
keys: ['orientation'],
render: (asset, $t) => String(asset.exifInfo?.orientation || $t('unknown')),
render: (asset, $t) => asset.exifInfo?.orientation || $t('unknown'),
},
{
icon: mdiPanorama,
@@ -240,7 +240,7 @@ const normalizeForComparison = (key: MetadataFieldKey, value: unknown): unknown
return value;
}
if (key === 'fileCreatedAt' || key === 'fileModifiedAt' || key === 'dateTimeOriginal' || key === 'modifyDate') {
if (['fileCreatedAt', 'fileModifiedAt', 'dateTimeOriginal', 'modifyDate'].includes(key)) {
const dateTime = DateTime.fromISO(String(value));
return dateTime.isValid ? dateTime.toISO() : String(value);
}
@@ -273,7 +273,7 @@ const getValueForAsset = (asset: AssetResponseDto, key: MetadataFieldKey): unkno
return getAssetResolution(asset);
}
default: {
if (asset.exifInfo && key in asset.exifInfo) {
if (asset.exifInfo && Object.hasOwn(asset.exifInfo, key)) {
return asset.exifInfo[key as keyof typeof asset.exifInfo];
}
return undefined;
+1 -1
View File
@@ -131,7 +131,7 @@ export const fileUploadHandler = async ({
};
function getDeviceAssetId(asset: File) {
return 'web' + '-' + asset.name + '-' + asset.lastModified;
return 'web-' + asset.name + '-' + asset.lastModified;
}
function hashFile(file: File): Promise<string> {
+1 -1
View File
@@ -47,7 +47,7 @@ export function handleError(error: unknown, localizedMessage: string, options?:
try {
let serverMessage = getServerErrorMessage(error);
if (serverMessage) {
serverMessage = `${String(serverMessage).slice(0, 75)}\n(Immich Server Error)`;
serverMessage = `${serverMessage.slice(0, 75)}\n(Immich Server Error)`;
}
const errorMessage = serverMessage || localizedMessage;
+1 -4
View File
@@ -30,10 +30,7 @@ export class InvocationTracker {
* @throws {Error} If the invocation is no longer valid
*/
isStillValid: () => {
if (invocation !== this.invocationsStarted) {
return false;
}
return true;
return invocation === this.invocationsStarted;
},
/**
+13 -11
View File
@@ -8,7 +8,7 @@ export type AssetGridRouteSearchParams = {
at: string | null | undefined;
};
export const isExternalUrl = (url: string): boolean => {
return new URL(url, globalThis.location.href).origin !== globalThis.location.origin;
return new URL(url, location.href).origin !== location.origin;
};
export const isPhotosRoute = (route?: string | null) => !!route?.startsWith('/(user)/photos/[[assetId=id]]');
@@ -33,11 +33,10 @@ function currentUrlWithoutAsset() {
// off / instead of a subpath, unlike every other asset-containing route.
if (isPhotosRoute(page.route.id)) {
return Route.photos() + page.url.search;
} else if (isSharedLinkSlugRoute(page.route.id)) {
return Route.viewSharedLink({ slug: page.data.slug, key: page.data.key }) + page.url.search;
} else {
return page.url.pathname.replace(/(\/photos.*)$/, '') + page.url.search;
}
return isSharedLinkSlugRoute(page.route.id)
? Route.viewSharedLink({ slug: page.data.slug, key: page.data.key }) + page.url.search
: page.url.pathname.replace(/(\/photos.*)$/, '') + page.url.search;
}
export function currentUrlReplaceAssetId(assetId: string) {
@@ -133,7 +132,8 @@ async function navigateAssetGridRoute(route: AssetGridRoute, options?: NavOption
export function navigate(change: ImmichRoute, options?: NavOptions): Promise<void> {
if (isAssetGridRoute(change)) {
return navigateAssetGridRoute(change, options);
} else if (isAssetRoute(change)) {
}
if (isAssetRoute(change)) {
return navigateAssetRoute(change, options);
}
// future navigation requests here
@@ -141,20 +141,22 @@ export function navigate(change: ImmichRoute, options?: NavOptions): Promise<voi
}
export const clearQueryParam = async (queryParam: string, url: URL) => {
if (url.searchParams.has(queryParam)) {
url.searchParams.delete(queryParam);
await goto(url, { keepFocus: true });
if (!url.searchParams.has(queryParam)) {
return;
}
url.searchParams.delete(queryParam);
await goto(url, { keepFocus: true });
};
export const getQueryValue = (queryKey: string) => {
const url = globalThis.location.href;
const url = location.href;
const urlObject = new URL(url);
return urlObject.searchParams.get(queryKey);
};
export const setQueryValue = async (queryKey: string, queryValue: string) => {
const url = globalThis.location.href;
const url = location.href;
const urlObject = new URL(url);
urlObject.searchParams.set(queryKey, queryValue);
await goto(urlObject, { keepFocus: true });
+1 -1
View File
@@ -17,7 +17,7 @@ export type OcrBox = {
};
const CJK_PATTERN =
/[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uAC00-\uD7AF\uFF00-\uFFEF]/;
/[\u{3000}-\u{303F}\u{3040}-\u{309F}\u{30A0}-\u{30FF}\u{3400}-\u{4DBF}\u{4E00}-\u{9FFF}\u{F900}-\u{FAFF}\u{AC00}-\u{D7AF}\u{FF00}-\u{FFEF}]/u;
const VERTICAL_ASPECT_RATIO = 1.5;
+1 -1
View File
@@ -1,5 +1,5 @@
export const removeAccents = (str: string) => {
return str.normalize('NFD').replaceAll(/[\u0300-\u036F]/g, '');
return str.normalize('NFD').replaceAll(/[\u{300}-\u{36F}]/gu, '');
};
export const normalizeSearchString = (str: string) => {
+2 -2
View File
@@ -95,8 +95,8 @@ export const fromTimelinePlainYearMonth = (timelineYearMonth: TimelineYearMonth)
) as DateTime<true>;
export const toISOYearMonthUTC = ({ year, month }: TimelineYearMonth): string => {
const yearFull = `${year}`.padStart(4, '0');
const monthFull = `${month}`.padStart(2, '0');
const yearFull = String(year).padStart(4, '0');
const monthFull = String(month).padStart(2, '0');
return `${yearFull}-${monthFull}-01T00:00:00.000Z`;
};
+9 -7
View File
@@ -29,14 +29,16 @@ export async function acquireWakeLock() {
}
export async function releaseWakeLock() {
if (sentinel) {
const toReleaseSentinel = sentinel;
// Unset first to avoid race condition after await
sentinel = undefined;
// eslint-disable-next-line tscompat/tscompat
await toReleaseSentinel.release();
if (!sentinel) {
return;
}
const toReleaseSentinel = sentinel;
// Unset first to avoid race condition after await
sentinel = undefined;
// eslint-disable-next-line tscompat/tscompat
await toReleaseSentinel.release();
}
if (isSupported) {
@@ -178,11 +178,13 @@
};
const updateThumbnailUsingCurrentSelection = async () => {
if (assetMultiSelectManager.assets.length === 1) {
const [firstAsset] = assetMultiSelectManager.assets;
assetMultiSelectManager.clear();
await updateThumbnail(firstAsset.id);
if (assetMultiSelectManager.assets.length !== 1) {
return;
}
const [firstAsset] = assetMultiSelectManager.assets;
assetMultiSelectManager.clear();
await updateThumbnail(firstAsset.id);
};
const updateThumbnail = async (assetId: string) => {
@@ -274,10 +276,12 @@
};
const onAlbumDelete = async ({ id }: AlbumResponseDto) => {
if (id === album.id) {
await goto(Route.albums());
viewMode = AlbumPageViewMode.VIEW;
if (id !== album.id) {
return;
}
await goto(Route.albums());
viewMode = AlbumPageViewMode.VIEW;
};
const onAlbumAddAssets = async ({ albumIds }: { albumIds: string[] }) => {

Some files were not shown because too many files have changed in this diff Show More