mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
chore(server,cli,web): housekeeping and stricter code style (#6751)
* add unicorn to eslint * fix lint errors for cli * fix merge * fix album name extraction * Update cli/src/commands/upload.command.ts Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> * es2k23 * use lowercase os * return undefined album name * fix bug in asset response dto * auto fix issues * fix server code style * es2022 and formatting * fix compilation error * fix test * fix config load * fix last lint errors * set string type * bump ts * start work on web * web formatting * Fix UUIDParamDto as UUIDParamDto * fix library service lint * fix web errors * fix errors * formatting * wip * lints fixed * web can now start * alphabetical package json * rename error * chore: clean up --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
e4d0560d49
commit
f44fa45aa0
218 changed files with 2471 additions and 1244 deletions
|
|
@ -14,10 +14,10 @@
|
|||
const deleteUser = async () => {
|
||||
try {
|
||||
const deletedUser = await api.userApi.deleteUser({ id: user.id });
|
||||
if (deletedUser.data.deletedAt != null) {
|
||||
dispatch('success');
|
||||
} else {
|
||||
if (deletedUser.data.deletedAt == undefined) {
|
||||
dispatch('fail');
|
||||
} else {
|
||||
dispatch('success');
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to delete user');
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
} from '@mdi/js';
|
||||
|
||||
export let title: string;
|
||||
export let subtitle: string | undefined = undefined;
|
||||
export let subtitle: string | undefined;
|
||||
export let jobCounts: JobCountsDto;
|
||||
export let queueStatus: QueueStatusDto;
|
||||
export let allowForceCommand = true;
|
||||
|
|
|
|||
|
|
@ -131,12 +131,13 @@
|
|||
jobs[jobId] = data;
|
||||
|
||||
switch (jobCommand.command) {
|
||||
case JobCommand.Empty:
|
||||
case JobCommand.Empty: {
|
||||
notificationController.show({
|
||||
message: `Cleared jobs for: ${title}`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, `Command '${jobCommand.command}' failed for job: ${title}`);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
const restoreUser = async () => {
|
||||
const restoredUser = await api.userApi.restoreUser({ id: user.id });
|
||||
if (restoredUser.data.deletedAt == null) {
|
||||
if (restoredUser.data.deletedAt == undefined) {
|
||||
dispatch('success');
|
||||
} else {
|
||||
dispatch('fail');
|
||||
|
|
|
|||
|
|
@ -19,11 +19,7 @@
|
|||
const dispatch = createEventDispatcher<{ save: void }>();
|
||||
|
||||
const handleReset = async (detail: SettingsEventType['reset']) => {
|
||||
if (detail.default) {
|
||||
await resetToDefault(detail.configKeys);
|
||||
} else {
|
||||
await reset(detail.configKeys);
|
||||
}
|
||||
await (detail.default ? resetToDefault(detail.configKeys) : reset(detail.configKeys));
|
||||
};
|
||||
|
||||
const handleSave = async (update: Partial<SystemConfigDto>) => {
|
||||
|
|
@ -47,7 +43,10 @@
|
|||
|
||||
const reset = async (configKeys: Array<keyof SystemConfigDto>) => {
|
||||
const { data: resetConfig } = await api.systemConfigApi.getConfig();
|
||||
config = configKeys.reduce((acc, key) => ({ ...acc, [key]: resetConfig[key] }), config);
|
||||
|
||||
for (const key of configKeys) {
|
||||
config = { ...config, [key]: resetConfig[key] };
|
||||
}
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset settings to the recent saved settings',
|
||||
|
|
@ -56,7 +55,9 @@
|
|||
};
|
||||
|
||||
const resetToDefault = async (configKeys: Array<keyof SystemConfigDto>) => {
|
||||
config = configKeys.reduce((acc, key) => ({ ...acc, [key]: defaultConfig[key] }), config);
|
||||
for (const key of configKeys) {
|
||||
config = { ...config, [key]: defaultConfig[key] };
|
||||
}
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset settings to default',
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@
|
|||
export let disabled = false;
|
||||
|
||||
function handleCheckboxChange(option: string) {
|
||||
if (value.includes(option)) {
|
||||
value = value.filter((item) => item !== option);
|
||||
} else {
|
||||
value = [...value, option];
|
||||
}
|
||||
value = value.includes(option) ? value.filter((item) => item !== option) : [...value, option];
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
const handleChange = (e: Event) => {
|
||||
value = (e.target as HTMLInputElement).value;
|
||||
if (number) {
|
||||
value = parseInt(value);
|
||||
value = Number.parseInt(value);
|
||||
}
|
||||
dispatch('select', value);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
$: parsedTemplate = () => {
|
||||
try {
|
||||
return renderTemplate(config.storageTemplate.template);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return 'error';
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,10 +122,10 @@ describe('AlbumCard component', () => {
|
|||
const onClickHandler = vi.fn();
|
||||
sut.component.$on('showalbumcontextmenu', onClickHandler);
|
||||
|
||||
const contextMenuBtnParent = sut.getByTestId('context-button-parent');
|
||||
const contextMenuButtonParent = sut.getByTestId('context-button-parent');
|
||||
|
||||
// Mock getBoundingClientRect to return a bounding rectangle that will result in the expected position
|
||||
contextMenuBtnParent.getBoundingClientRect = () => ({
|
||||
contextMenuButtonParent.getBoundingClientRect = () => ({
|
||||
x: 123,
|
||||
y: 456,
|
||||
width: 0,
|
||||
|
|
@ -138,7 +138,7 @@ describe('AlbumCard component', () => {
|
|||
});
|
||||
|
||||
await fireEvent(
|
||||
contextMenuBtnParent,
|
||||
contextMenuButtonParent,
|
||||
new MouseEvent('click', {
|
||||
clientX: 123,
|
||||
clientY: 456,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
const dispatchShowContextMenu = createEventDispatcher<OnShowContextMenu>();
|
||||
|
||||
const loadHighQualityThumbnail = async (thubmnailId: string | null) => {
|
||||
if (thubmnailId == null) {
|
||||
if (thubmnailId == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,11 +83,12 @@
|
|||
}
|
||||
if (!$showAssetViewer) {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
case 'Escape': {
|
||||
if ($isMultiSelectState) {
|
||||
assetInteractionStore.clearMultiselect();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
try {
|
||||
const { data } = await api.userApi.getMyUserInfo();
|
||||
currentUser = data;
|
||||
} catch (e) {
|
||||
handleError(e, 'Unable to refresh user');
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to refresh user');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -58,8 +58,8 @@
|
|||
dispatch('remove', userId);
|
||||
const message = userId === 'me' ? `Left ${album.albumName}` : `Removed ${selectedRemoveUser.name}`;
|
||||
notificationController.show({ type: NotificationType.Info, message });
|
||||
} catch (e) {
|
||||
handleError(e, 'Unable to remove user');
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to remove user');
|
||||
} finally {
|
||||
selectedRemoveUser = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@
|
|||
}>();
|
||||
|
||||
$: isSelected = (id: string): boolean | undefined => {
|
||||
if (!selectedThumbnail && album.albumThumbnailAssetId == id) {
|
||||
return true;
|
||||
} else {
|
||||
return selectedThumbnail?.id == id;
|
||||
}
|
||||
return !selectedThumbnail && album.albumThumbnailAssetId == id ? true : selectedThumbnail?.id == id;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@
|
|||
users = data.filter((user) => !(user.deletedAt || user.id === album.ownerId));
|
||||
|
||||
// Remove the existed shared users from the album
|
||||
album.sharedUsers.forEach((sharedUser) => {
|
||||
for (const sharedUser of album.sharedUsers) {
|
||||
users = users.filter((user) => user.id !== sharedUser.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const getSharedLinks = async () => {
|
||||
|
|
@ -40,11 +40,9 @@
|
|||
};
|
||||
|
||||
const handleSelect = (user: UserResponseDto) => {
|
||||
if (selectedUsers.includes(user)) {
|
||||
selectedUsers = selectedUsers.filter((selectedUser) => selectedUser.id !== user.id);
|
||||
} else {
|
||||
selectedUsers = [...selectedUsers, user];
|
||||
}
|
||||
selectedUsers = selectedUsers.includes(user)
|
||||
? selectedUsers.filter((selectedUser) => selectedUser.id !== user.id)
|
||||
: [...selectedUsers, user];
|
||||
};
|
||||
|
||||
const handleUnselect = (user: UserResponseDto) => {
|
||||
|
|
@ -122,7 +120,7 @@
|
|||
size="sm"
|
||||
fullwidth
|
||||
rounded="full"
|
||||
disabled={!selectedUsers.length}
|
||||
disabled={selectedUsers.length === 0}
|
||||
on:click={() => dispatch('select', selectedUsers)}>Add</Button
|
||||
>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
close: void;
|
||||
}>();
|
||||
|
||||
$: showDeleteReaction = Array(reactions.length).fill(false);
|
||||
$: showDeleteReaction = Array.from({ length: reactions.length }).fill(false);
|
||||
$: {
|
||||
if (innerHeight && activityHeight) {
|
||||
divHeight = innerHeight - activityHeight;
|
||||
|
|
@ -198,7 +198,7 @@
|
|||
{/if}
|
||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||
<div class="flex items-start w-fit pt-[5px]" title="Delete comment">
|
||||
<button on:click={() => (!showDeleteReaction[index] ? showOptionsMenu(index) : '')}>
|
||||
<button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
|
||||
<Icon path={mdiDotsVertical} />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
{/if}
|
||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||
<div class="flex items-start w-fit" title="Delete like">
|
||||
<button on:click={() => (!showDeleteReaction[index] ? showOptionsMenu(index) : '')}>
|
||||
<button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
|
||||
<Icon path={mdiDotsVertical} />
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -145,11 +145,7 @@
|
|||
albumId: album.id,
|
||||
type: ReactionType.Like,
|
||||
});
|
||||
if (data.length > 0) {
|
||||
isLiked = data[0];
|
||||
} else {
|
||||
isLiked = null;
|
||||
}
|
||||
isLiked = data.length > 0 ? data[0] : null;
|
||||
} catch (error) {
|
||||
handleError(error, "Can't get Favorite");
|
||||
}
|
||||
|
|
@ -238,8 +234,8 @@
|
|||
try {
|
||||
const { data } = await api.albumApi.getAllAlbums({ assetId: asset.id });
|
||||
appearsInAlbums = data;
|
||||
} catch (e) {
|
||||
console.error('Error getting album that asset belong to', e);
|
||||
} catch (error) {
|
||||
console.error('Error getting album that asset belong to', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -260,40 +256,48 @@
|
|||
|
||||
switch (key) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'A': {
|
||||
if (shiftKey) {
|
||||
toggleArchive();
|
||||
}
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
}
|
||||
case 'ArrowLeft': {
|
||||
navigateAssetBackward();
|
||||
return;
|
||||
case 'ArrowRight':
|
||||
}
|
||||
case 'ArrowRight': {
|
||||
navigateAssetForward();
|
||||
return;
|
||||
}
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'D': {
|
||||
if (shiftKey) {
|
||||
downloadFile(asset);
|
||||
}
|
||||
return;
|
||||
case 'Delete':
|
||||
}
|
||||
case 'Delete': {
|
||||
trashOrDelete(shiftKey);
|
||||
return;
|
||||
case 'Escape':
|
||||
}
|
||||
case 'Escape': {
|
||||
if (isShowDeleteConfirmation) {
|
||||
isShowDeleteConfirmation = false;
|
||||
return;
|
||||
}
|
||||
closeViewer();
|
||||
return;
|
||||
case 'f':
|
||||
}
|
||||
case 'f': {
|
||||
toggleFavorite();
|
||||
return;
|
||||
case 'i':
|
||||
}
|
||||
case 'i': {
|
||||
isShowActivity = false;
|
||||
$isShowDetail = !$isShowDetail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -383,8 +387,8 @@
|
|||
message: 'Moved to trash',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(e, 'Unable to trash asset');
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to trash asset');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -398,8 +402,8 @@
|
|||
message: 'Permanently deleted asset',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(e, 'Unable to delete asset');
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to delete asset');
|
||||
} finally {
|
||||
isShowDeleteConfirmation = false;
|
||||
}
|
||||
|
|
@ -537,11 +541,7 @@
|
|||
const handleStackedAssetMouseEvent = (e: CustomEvent<{ isMouseOver: boolean }>, asset: AssetResponseDto) => {
|
||||
const { isMouseOver } = e.detail;
|
||||
|
||||
if (isMouseOver) {
|
||||
previewStackedAsset = asset;
|
||||
} else {
|
||||
previewStackedAsset = undefined;
|
||||
}
|
||||
previewStackedAsset = isMouseOver ? asset : undefined;
|
||||
};
|
||||
|
||||
const handleUnstack = async () => {
|
||||
|
|
|
|||
|
|
@ -108,10 +108,11 @@
|
|||
}
|
||||
const ctrl = event.ctrlKey;
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
case 'Enter': {
|
||||
if (ctrl && event.target === textArea) {
|
||||
handleFocusOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -222,7 +223,7 @@
|
|||
bind:this={textArea}
|
||||
class="max-h-[500px]
|
||||
w-full resize-none overflow-hidden border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary"
|
||||
placeholder={!isOwner ? '' : 'Add a description'}
|
||||
placeholder={isOwner ? 'Add a description' : ''}
|
||||
on:focusin={handleFocusIn}
|
||||
on:focusout={handleFocusOut}
|
||||
on:input={() => autoGrowHeight(textArea)}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@
|
|||
dataUrl = URL.createObjectURL(data);
|
||||
return dataUrl;
|
||||
} else {
|
||||
throw new Error('Invalid data format');
|
||||
throw new TypeError('Invalid data format');
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
errorMessage = 'Failed to load asset';
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
let assetData: string;
|
||||
let abortController: AbortController;
|
||||
let hasZoomed = false;
|
||||
let copyImageToClipboard: (src: string) => Promise<Blob>;
|
||||
let copyImageToClipboard: (source: string) => Promise<Blob>;
|
||||
let canCopyImagesToClipboard: () => boolean;
|
||||
|
||||
$: if (imgElement) {
|
||||
|
|
@ -90,8 +90,8 @@
|
|||
message: 'Copied image to clipboard.',
|
||||
timeout: 3000,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error [photo-viewer]:', err);
|
||||
} catch (error) {
|
||||
console.error('Error [photo-viewer]:', error);
|
||||
notificationController.show({
|
||||
type: NotificationType.Error,
|
||||
message: 'Copying image to clipboard failed.',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { onMount, tick } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { thumbHashToDataURL } from 'thumbhash';
|
||||
// eslint-disable-next-line unicorn/prefer-node-protocol
|
||||
import { Buffer } from 'buffer';
|
||||
import { mdiEyeOffOutline } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@
|
|||
|
||||
{#if asset.stackCount && showStackedIcon}
|
||||
<div
|
||||
class="absolute {asset.type == AssetTypeEnum.Image && asset.livePhotoVideoId == null
|
||||
class="absolute {asset.type == AssetTypeEnum.Image && asset.livePhotoVideoId == undefined
|
||||
? 'top-0 right-0'
|
||||
: 'top-7 right-1'} z-20 flex place-items-center gap-1 text-xs font-medium text-white"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
export let options: T[];
|
||||
export let selectedOption = options[0];
|
||||
|
||||
export let render: (item: T) => string | RenderedOption = (item) => String(item);
|
||||
export let render: (item: T) => string | RenderedOption = String;
|
||||
|
||||
type RenderedOption = {
|
||||
title: string;
|
||||
|
|
@ -54,13 +54,15 @@
|
|||
const renderOption = (option: T): RenderedOption => {
|
||||
const renderedOption = render(option);
|
||||
switch (typeof renderedOption) {
|
||||
case 'string':
|
||||
case 'string': {
|
||||
return { title: renderedOption };
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
title: renderedOption.title,
|
||||
icon: renderedOption.icon,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@
|
|||
img.src = data;
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
img.onload = () => resolve();
|
||||
img.onerror = () => resolve();
|
||||
img.addEventListener('load', () => resolve());
|
||||
img.addEventListener('error', () => resolve());
|
||||
});
|
||||
|
||||
image = img;
|
||||
|
|
@ -56,13 +56,20 @@
|
|||
if (image === null) {
|
||||
return null;
|
||||
}
|
||||
const { boundingBoxX1: x1, boundingBoxX2: x2, boundingBoxY1: y1, boundingBoxY2: y2 } = face;
|
||||
const {
|
||||
boundingBoxX1: x1,
|
||||
boundingBoxX2: x2,
|
||||
boundingBoxY1: y1,
|
||||
boundingBoxY2: y2,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
} = face;
|
||||
|
||||
const coordinates = {
|
||||
x1: (image.naturalWidth / face.imageWidth) * x1,
|
||||
x2: (image.naturalWidth / face.imageWidth) * x2,
|
||||
y1: (image.naturalHeight / face.imageHeight) * y1,
|
||||
y2: (image.naturalHeight / face.imageHeight) * y2,
|
||||
x1: (image.naturalWidth / imageWidth) * x1,
|
||||
x2: (image.naturalWidth / imageWidth) * x2,
|
||||
y1: (image.naturalHeight / imageHeight) * y1,
|
||||
y2: (image.naturalHeight / imageHeight) * y2,
|
||||
};
|
||||
|
||||
const faceWidth = coordinates.x2 - coordinates.x1;
|
||||
|
|
@ -72,17 +79,17 @@
|
|||
faceImage.src = image.src;
|
||||
|
||||
await new Promise((resolve) => {
|
||||
faceImage.onload = resolve;
|
||||
faceImage.onerror = () => resolve(null);
|
||||
faceImage.addEventListener('load', resolve);
|
||||
faceImage.addEventListener('error', () => resolve(null));
|
||||
});
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = faceWidth;
|
||||
canvas.height = faceHeight;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(faceImage, coordinates.x1, coordinates.y1, faceWidth, faceHeight, 0, 0, faceWidth, faceHeight);
|
||||
const context = canvas.getContext('2d');
|
||||
if (context) {
|
||||
context.drawImage(faceImage, coordinates.x1, coordinates.y1, faceWidth, faceHeight, 0, 0, faceWidth, faceHeight);
|
||||
|
||||
return canvas.toDataURL();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
}}
|
||||
>
|
||||
<ImageThumbnail
|
||||
border={potentialMergePeople.length !== 0}
|
||||
border={potentialMergePeople.length > 0}
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(personMerge2.id)}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,8 @@
|
|||
people = peopleCopy;
|
||||
return;
|
||||
}
|
||||
if (!force) {
|
||||
if (people.length < maximumLengthSearchPeople && name.startsWith(searchWord)) {
|
||||
return;
|
||||
}
|
||||
if (!force && people.length < maximumLengthSearchPeople && name.startsWith(searchWord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => (isSearchingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
allPeople = data.people;
|
||||
const result = await api.faceApi.getFaces({ id: assetId });
|
||||
peopleWithFaces = result.data;
|
||||
selectedPersonToCreate = new Array<string | null>(peopleWithFaces.length);
|
||||
selectedPersonToReassign = new Array<PersonResponseDto | null>(peopleWithFaces.length);
|
||||
selectedPersonToCreate = Array.from({ length: peopleWithFaces.length });
|
||||
selectedPersonToReassign = Array.from({ length: peopleWithFaces.length });
|
||||
} catch (error) {
|
||||
handleError(error, "Can't get faces");
|
||||
} finally {
|
||||
|
|
@ -106,20 +106,20 @@
|
|||
selectedPersonToReassign.filter((person) => person !== null).length;
|
||||
if (numberOfChanges > 0) {
|
||||
try {
|
||||
for (let i = 0; i < peopleWithFaces.length; i++) {
|
||||
const personId = selectedPersonToReassign[i]?.id;
|
||||
for (const [index, peopleWithFace] of peopleWithFaces.entries()) {
|
||||
const personId = selectedPersonToReassign[index]?.id;
|
||||
|
||||
if (personId) {
|
||||
await api.faceApi.reassignFacesById({
|
||||
id: personId,
|
||||
faceDto: { id: peopleWithFaces[i].id },
|
||||
faceDto: { id: peopleWithFace.id },
|
||||
});
|
||||
} else if (selectedPersonToCreate[i]) {
|
||||
} else if (selectedPersonToCreate[index]) {
|
||||
const { data } = await api.personApi.createPerson();
|
||||
numberOfPersonToCreate.push(data.id);
|
||||
await api.faceApi.reassignFacesById({
|
||||
id: data.id,
|
||||
faceDto: { id: peopleWithFaces[i].id },
|
||||
faceDto: { id: peopleWithFace.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
clearTimeout(loaderLoadingDoneTimeout);
|
||||
dispatch('refresh');
|
||||
} else {
|
||||
automaticRefreshTimeout = setTimeout(() => dispatch('refresh'), 15000);
|
||||
automaticRefreshTimeout = setTimeout(() => dispatch('refresh'), 15_000);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
let confirmPassowrd = '';
|
||||
|
||||
let canCreateUser = false;
|
||||
let quotaSize: number | undefined = undefined;
|
||||
let quotaSize: number | undefined;
|
||||
let isCreatingUser = false;
|
||||
|
||||
$: quotaSizeWarning = quotaSize && convertToBytes(Number(quotaSize), 'GiB') > $serverInfo.diskSizeRaw;
|
||||
|
|
@ -69,11 +69,10 @@
|
|||
error = 'Error create user account';
|
||||
isCreatingUser = false;
|
||||
}
|
||||
} catch (e) {
|
||||
error = 'Error create user account';
|
||||
} catch (error) {
|
||||
isCreatingUser = false;
|
||||
|
||||
console.log('[ERROR] registerUser', e);
|
||||
console.log('[ERROR] registerUser', error);
|
||||
|
||||
notificationController.show({
|
||||
message: `Error create new user, check console for more detail`,
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@
|
|||
if (status == 200) {
|
||||
dispatch('resetPasswordSuccess');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error reseting user password', e);
|
||||
} catch (error) {
|
||||
console.error('Error reseting user password', error);
|
||||
notificationController.show({
|
||||
message: 'Error reseting user password, check console for more details',
|
||||
type: NotificationType.Error,
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if editImportPath != null}
|
||||
{#if editImportPath != undefined}
|
||||
<LibraryImportPathForm
|
||||
title="Edit Import Path"
|
||||
submitText="Save"
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if editExclusionPattern != null}
|
||||
{#if editExclusionPattern != undefined}
|
||||
<LibraryExclusionPatternForm
|
||||
submitText="Save"
|
||||
canDelete={true}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@
|
|||
await oauth.login(window.location);
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error [login-form] [oauth.callback]', e);
|
||||
oauthError = (await getServerErrorMessage(e)) || 'Unable to complete OAuth login';
|
||||
} catch (error) {
|
||||
console.error('Error [login-form] [oauth.callback]', error);
|
||||
oauthError = (await getServerErrorMessage(error)) || 'Unable to complete OAuth login';
|
||||
oauthLoading = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
import { tweened } from 'svelte/motion';
|
||||
import { mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiPause, mdiPlay } from '@mdi/js';
|
||||
|
||||
const parseIndex = (s: string | null, max: number | null) => Math.max(Math.min(parseInt(s ?? '') || 0, max ?? 0), 0);
|
||||
const parseIndex = (s: string | null, max: number | null) =>
|
||||
Math.max(Math.min(Number.parseInt(s ?? '') || 0, max ?? 0), 0);
|
||||
|
||||
$: memoryIndex = parseIndex($page.url.searchParams.get(QueryParameter.MEMORY_INDEX), $memoryStore?.length - 1);
|
||||
$: assetIndex = parseIndex($page.url.searchParams.get(QueryParameter.ASSET_INDEX), currentMemory?.assets.length - 1);
|
||||
|
|
@ -114,18 +115,19 @@
|
|||
<div class="flex place-content-center place-items-center gap-2 overflow-hidden">
|
||||
<CircleIconButton icon={paused ? mdiPlay : mdiPause} forceDark on:click={() => (paused = !paused)} />
|
||||
|
||||
{#each currentMemory.assets as _, i}
|
||||
{#each currentMemory.assets as _, index}
|
||||
<button
|
||||
class="relative w-full py-2"
|
||||
on:click={() => goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex}&${QueryParameter.ASSET_INDEX}=${i}`)}
|
||||
on:click={() =>
|
||||
goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex}&${QueryParameter.ASSET_INDEX}=${index}`)}
|
||||
>
|
||||
<span class="absolute left-0 h-[2px] w-full bg-gray-500" />
|
||||
{#await resetPromise}
|
||||
<span class="absolute left-0 h-[2px] bg-white" style:width={`${i < assetIndex ? 100 : 0}%`} />
|
||||
<span class="absolute left-0 h-[2px] bg-white" style:width={`${index < assetIndex ? 100 : 0}%`} />
|
||||
{:then}
|
||||
<span
|
||||
class="absolute left-0 h-[2px] bg-white"
|
||||
style:width={`${i < assetIndex ? 100 : i > assetIndex ? 0 : $progress * 100}%`}
|
||||
style:width={`${index < assetIndex ? 100 : index > assetIndex ? 0 : $progress * 100}%`}
|
||||
/>
|
||||
{/await}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
const handleAddToNewAlbum = (albumName: string) => {
|
||||
showAlbumPicker = false;
|
||||
|
||||
const assetIds = Array.from(getAssets()).map((asset) => asset.id);
|
||||
const assetIds = [...getAssets()].map((asset) => asset.id);
|
||||
api.albumApi.createAlbum({ createAlbumDto: { albumName, assetIds } }).then((response) => {
|
||||
const { id, albumName } = response.data;
|
||||
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
const handleAddToAlbum = async (album: AlbumResponseDto) => {
|
||||
showAlbumPicker = false;
|
||||
const assetIds = Array.from(getAssets()).map((asset) => asset.id);
|
||||
const assetIds = [...getAssets()].map((asset) => asset.id);
|
||||
await addAssetsToAlbum(album.id, assetIds);
|
||||
clearSelect();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
loading = true;
|
||||
|
||||
try {
|
||||
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isArchived !== isArchived);
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
|
||||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
if (ids.length > 0) {
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
$: isAllVideos = Array.from(getOwnedAssets()).every((asset) => asset.type === AssetTypeEnum.Video);
|
||||
$: isAllVideos = [...getOwnedAssets()].every((asset) => asset.type === AssetTypeEnum.Video);
|
||||
|
||||
const handleRunJob = async (name: AssetJobName) => {
|
||||
try {
|
||||
const ids = Array.from(getOwnedAssets()).map(({ id }) => id);
|
||||
const ids = [...getOwnedAssets()].map(({ id }) => id);
|
||||
await api.assetApi.runAssetJobs({ assetJobsDto: { assetIds: ids, name } });
|
||||
notificationController.show({ message: api.getAssetJobMessage(name), type: NotificationType.Info });
|
||||
clearSelect();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
{#if showModal}
|
||||
<CreateSharedLinkModal
|
||||
assetIds={Array.from(getAssets()).map(({ id }) => id)}
|
||||
assetIds={[...getAssets()].map(({ id }) => id)}
|
||||
on:close={() => (showModal = false)}
|
||||
on:escape={escape}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@
|
|||
|
||||
const handleDelete = async () => {
|
||||
loading = true;
|
||||
const ids = Array.from(getOwnedAssets())
|
||||
.filter((a) => !a.isExternal)
|
||||
.map((a) => a.id);
|
||||
const ids = [...getOwnedAssets()].filter((a) => !a.isExternal).map((a) => a.id);
|
||||
await deleteAssets(force, onAssetDelete, ids);
|
||||
clearSelect();
|
||||
isShowConfirmation = false;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
const handleDownloadFiles = async () => {
|
||||
const assets = Array.from(getAssets());
|
||||
const assets = [...getAssets()];
|
||||
if (assets.length === 1) {
|
||||
clearSelect();
|
||||
await downloadFile(assets[0]);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
loading = true;
|
||||
|
||||
try {
|
||||
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isFavorite !== isFavorite);
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
||||
|
||||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import { mdiDeleteOutline } from '@mdi/js';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let onRemove: ((assetIds: string[]) => void) | undefined = undefined;
|
||||
export let onRemove: ((assetIds: string[]) => void) | undefined;
|
||||
export let menuItem = false;
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
const removeFromAlbum = async () => {
|
||||
try {
|
||||
const ids = Array.from(getAssets()).map((a) => a.id);
|
||||
const ids = [...getAssets()].map((a) => a.id);
|
||||
const { data: results } = await api.albumApi.removeAssetFromAlbum({
|
||||
id: album.id,
|
||||
bulkIdsDto: { ids },
|
||||
|
|
@ -38,8 +38,8 @@
|
|||
});
|
||||
|
||||
clearSelect();
|
||||
} catch (e) {
|
||||
console.error('Error [album-viewer] [removeAssetFromAlbum]', e);
|
||||
} catch (error) {
|
||||
console.error('Error [album-viewer] [removeAssetFromAlbum]', error);
|
||||
notificationController.show({
|
||||
type: NotificationType.Error,
|
||||
message: 'Error removing assets from album, check console for more details',
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
const { data: results } = await api.sharedLinkApi.removeSharedLinkAssets({
|
||||
id: sharedLink.id,
|
||||
assetIdsDto: {
|
||||
assetIds: Array.from(getAssets()).map((asset) => asset.id),
|
||||
assetIds: [...getAssets()].map((asset) => asset.id),
|
||||
},
|
||||
key: api.getKey(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import { mdiHistory } from '@mdi/js';
|
||||
import type { OnRestore } from '$lib/utils/actions';
|
||||
|
||||
export let onRestore: OnRestore | undefined = undefined;
|
||||
export let onRestore: OnRestore | undefined;
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
loading = true;
|
||||
|
||||
try {
|
||||
const ids = Array.from(getAssets()).map((a) => a.id);
|
||||
const ids = [...getAssets()].map((a) => a.id);
|
||||
await api.trashApi.restoreAssets({ bulkIdsDto: { ids } });
|
||||
onRestore?.(ids);
|
||||
|
||||
|
|
@ -31,8 +31,8 @@
|
|||
});
|
||||
|
||||
clearSelect();
|
||||
} catch (e) {
|
||||
handleError(e, 'Error restoring assets');
|
||||
} catch (error) {
|
||||
handleError(error, 'Error restoring assets');
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@
|
|||
}
|
||||
|
||||
selecting = false;
|
||||
} catch (e) {
|
||||
handleError(e, 'Error selecting all assets');
|
||||
} catch (error) {
|
||||
handleError(error, 'Error selecting all assets');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
import { handleError } from '$lib/utils/handle-error';
|
||||
import type { OnStack } from '$lib/utils/actions';
|
||||
|
||||
export let onStack: OnStack | undefined = undefined;
|
||||
export let onStack: OnStack | undefined;
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleStack = async () => {
|
||||
try {
|
||||
const assets = Array.from(getOwnedAssets());
|
||||
const assets = [...getOwnedAssets()];
|
||||
const parent = assets.at(0);
|
||||
|
||||
if (parent == undefined) {
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
for (const asset of children) {
|
||||
asset.stackParentId = parent?.id;
|
||||
// Add grand-children's count to new parent
|
||||
childrenCount += asset.stackCount == null ? 1 : asset.stackCount + 1;
|
||||
childrenCount += asset.stackCount == undefined ? 1 : asset.stackCount + 1;
|
||||
// Reset children stack info
|
||||
asset.stackCount = null;
|
||||
asset.stack = [];
|
||||
|
|
|
|||
|
|
@ -48,13 +48,16 @@
|
|||
$: geometry = (() => {
|
||||
const geometry = [];
|
||||
for (let group of assetsGroupByDate) {
|
||||
const justifiedLayoutResult = justifiedLayout(group.map(getAssetRatio), {
|
||||
boxSpacing: 2,
|
||||
containerWidth: Math.floor(viewport.width),
|
||||
containerPadding: 0,
|
||||
targetRowHeightTolerance: 0.15,
|
||||
targetRowHeight: 235,
|
||||
});
|
||||
const justifiedLayoutResult = justifiedLayout(
|
||||
group.map((assetGroup) => getAssetRatio(assetGroup)),
|
||||
{
|
||||
boxSpacing: 2,
|
||||
containerWidth: Math.floor(viewport.width),
|
||||
containerPadding: 0,
|
||||
targetRowHeightTolerance: 0.15,
|
||||
targetRowHeight: 235,
|
||||
},
|
||||
);
|
||||
geometry.push({
|
||||
...justifiedLayoutResult,
|
||||
containerWidth: calculateWidth(justifiedLayoutResult.boxes),
|
||||
|
|
|
|||
|
|
@ -44,9 +44,7 @@
|
|||
|
||||
$: timelineY = element?.scrollTop || 0;
|
||||
$: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0;
|
||||
$: idsSelectedAssets = Array.from($selectedAssets)
|
||||
.filter((a) => !a.isExternal)
|
||||
.map((a) => a.id);
|
||||
$: idsSelectedAssets = [...$selectedAssets].filter((a) => !a.isExternal).map((a) => a.id);
|
||||
|
||||
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
|
||||
const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>();
|
||||
|
|
@ -86,20 +84,23 @@
|
|||
|
||||
if (!$showAssetViewer) {
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
case 'Escape': {
|
||||
dispatch('escape');
|
||||
return;
|
||||
case '?':
|
||||
}
|
||||
case '?': {
|
||||
if (event.shiftKey) {
|
||||
event.preventDefault();
|
||||
showShortcuts = !showShortcuts;
|
||||
}
|
||||
return;
|
||||
case '/':
|
||||
}
|
||||
case '/': {
|
||||
event.preventDefault();
|
||||
goto(AppRoute.EXPLORE);
|
||||
return;
|
||||
case 'Delete':
|
||||
}
|
||||
case 'Delete': {
|
||||
if ($isMultiSelectState) {
|
||||
let force = false;
|
||||
if (shiftKey || !isTrashEnabled) {
|
||||
|
|
@ -113,6 +114,7 @@
|
|||
trashOrDelete(force);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -124,8 +126,8 @@
|
|||
};
|
||||
|
||||
function intersectedHandler(event: CustomEvent) {
|
||||
const el = event.detail.container as HTMLElement;
|
||||
const target = el.firstChild as HTMLElement;
|
||||
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);
|
||||
|
|
@ -160,24 +162,27 @@
|
|||
switch (action) {
|
||||
case removeAction:
|
||||
case AssetAction.TRASH:
|
||||
case AssetAction.DELETE:
|
||||
case AssetAction.DELETE: {
|
||||
// find the next asset to show or close the viewer
|
||||
(await handleNext()) || (await handlePrevious()) || handleClose();
|
||||
|
||||
// delete after find the next one
|
||||
assetStore.removeAsset(asset.id);
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetAction.ARCHIVE:
|
||||
case AssetAction.UNARCHIVE:
|
||||
case AssetAction.FAVORITE:
|
||||
case AssetAction.UNFAVORITE:
|
||||
case AssetAction.UNFAVORITE: {
|
||||
assetStore.updateAsset(asset);
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetAction.ADD:
|
||||
case AssetAction.ADD: {
|
||||
assetStore.addAsset(asset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -392,7 +397,7 @@
|
|||
<div class="mt-8 animate-pulse">
|
||||
<div class="mb-2 h-4 w-24 rounded-full bg-immich-primary/20 dark:bg-immich-dark-primary/20" />
|
||||
<div class="flex w-[120%] flex-wrap">
|
||||
{#each Array(100) as _}
|
||||
{#each Array.from({ length: 100 }) as _}
|
||||
<div class="m-[1px] h-[10em] w-[16em] bg-immich-primary/20 dark:bg-immich-dark-primary/20" />
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
setContext({
|
||||
getAssets: () => assets,
|
||||
getOwnedAssets: () =>
|
||||
ownerId !== undefined ? new Set(Array.from(assets).filter((asset) => asset.ownerId === ownerId)) : assets,
|
||||
ownerId === undefined ? assets : new Set([...assets].filter((asset) => asset.ownerId === ownerId)),
|
||||
clearSelect,
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@
|
|||
{/if}
|
||||
|
||||
<div class="inline-block" bind:offsetWidth={innerWidth}>
|
||||
{#each $memoryStore as memory, i (memory.title)}
|
||||
{#each $memoryStore as memory, index (memory.title)}
|
||||
<button
|
||||
class="memory-card relative mr-8 inline-block aspect-video h-[215px] rounded-xl"
|
||||
on:click={() => goto(`${AppRoute.MEMORY}?${QueryParameter.MEMORY_INDEX}=${i}`)}
|
||||
on:click={() => goto(`${AppRoute.MEMORY}?${QueryParameter.MEMORY_INDEX}=${index}`)}
|
||||
>
|
||||
<img
|
||||
class="h-full w-full rounded-xl object-cover"
|
||||
|
|
|
|||
|
|
@ -38,11 +38,9 @@
|
|||
const handleUploadAssets = async (files: File[] = []) => {
|
||||
try {
|
||||
let results: (string | undefined)[] = [];
|
||||
if (!files || files.length === 0 || !Array.isArray(files)) {
|
||||
results = await openFileUploadDialog(undefined);
|
||||
} else {
|
||||
results = await fileUploadHandler(files, undefined);
|
||||
}
|
||||
results = await (!files || files.length === 0 || !Array.isArray(files)
|
||||
? openFileUploadDialog()
|
||||
: fileUploadHandler(files));
|
||||
const { data } = await api.sharedLinkApi.addSharedLinkAssets({
|
||||
id: sharedLink.id,
|
||||
assetIdsDto: {
|
||||
|
|
@ -57,8 +55,8 @@
|
|||
message: `Added ${added} assets`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (e) {
|
||||
await handleError(e, 'Unable to add assets to shared link');
|
||||
} catch (error) {
|
||||
await handleError(error, 'Unable to add assets to shared link');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@
|
|||
});
|
||||
|
||||
$: {
|
||||
if (search.length > 0 && albums.length > 0) {
|
||||
filteredAlbums = albums.filter((album) => {
|
||||
return album.albumName.toLowerCase().includes(search.toLowerCase());
|
||||
});
|
||||
} else {
|
||||
filteredAlbums = albums;
|
||||
}
|
||||
filteredAlbums =
|
||||
search.length > 0 && albums.length > 0
|
||||
? albums.filter((album) => {
|
||||
return album.albumName.toLowerCase().includes(search.toLowerCase());
|
||||
})
|
||||
: albums;
|
||||
}
|
||||
|
||||
const handleSelect = (album: AlbumResponseDto) => {
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@
|
|||
if (browser) {
|
||||
const scrollTop = document.documentElement.scrollTop;
|
||||
const scrollLeft = document.documentElement.scrollLeft;
|
||||
window.onscroll = function () {
|
||||
window.addEventListener('scroll', function () {
|
||||
window.scrollTo(scrollLeft, scrollTop);
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (browser) {
|
||||
window.onscroll = null;
|
||||
window.addEventListener('scroll', () => {});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@
|
|||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!point) {
|
||||
dispatch('cancel');
|
||||
} else {
|
||||
if (point) {
|
||||
dispatch('confirm', point);
|
||||
} else {
|
||||
dispatch('cancel');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
|
||||
const handleCreateSharedLink = async () => {
|
||||
const expirationTime = getExpirationTimeInMillisecond();
|
||||
const currentTime = new Date().getTime();
|
||||
const currentTime = Date.now();
|
||||
const expirationDate = expirationTime ? new Date(currentTime + expirationTime).toISOString() : undefined;
|
||||
|
||||
try {
|
||||
|
|
@ -84,8 +84,8 @@
|
|||
},
|
||||
});
|
||||
sharedLink = makeSharedLinkUrl($serverConfig.externalDomain, data.key);
|
||||
} catch (e) {
|
||||
handleError(e, 'Failed to create shared link');
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to create shared link');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -99,20 +99,27 @@
|
|||
|
||||
const getExpirationTimeInMillisecond = () => {
|
||||
switch (expirationTime) {
|
||||
case '30 minutes':
|
||||
case '30 minutes': {
|
||||
return 30 * 60 * 1000;
|
||||
case '1 hour':
|
||||
}
|
||||
case '1 hour': {
|
||||
return 60 * 60 * 1000;
|
||||
case '6 hours':
|
||||
}
|
||||
case '6 hours': {
|
||||
return 6 * 60 * 60 * 1000;
|
||||
case '1 day':
|
||||
}
|
||||
case '1 day': {
|
||||
return 24 * 60 * 60 * 1000;
|
||||
case '7 days':
|
||||
}
|
||||
case '7 days': {
|
||||
return 7 * 24 * 60 * 60 * 1000;
|
||||
case '30 days':
|
||||
}
|
||||
case '30 days': {
|
||||
return 30 * 24 * 60 * 60 * 1000;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -123,7 +130,7 @@
|
|||
|
||||
try {
|
||||
const expirationTime = getExpirationTimeInMillisecond();
|
||||
const currentTime = new Date().getTime();
|
||||
const currentTime = Date.now();
|
||||
const expirationDate: string | null = expirationTime
|
||||
? new Date(currentTime + expirationTime).toISOString()
|
||||
: null;
|
||||
|
|
@ -146,8 +153,8 @@
|
|||
});
|
||||
|
||||
dispatch('close');
|
||||
} catch (e) {
|
||||
handleError(e, 'Failed to edit shared link');
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to edit shared link');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
export let fullWidth = false;
|
||||
export let src = empty1Url;
|
||||
|
||||
const noop = () => undefined;
|
||||
const noop = () => {};
|
||||
|
||||
$: handler = actionHandler || noop;
|
||||
$: width = fullWidth ? 'w-full' : 'w-[50%]';
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@
|
|||
|
||||
const selectAssetHandler = (event: CustomEvent) => {
|
||||
const { asset }: { asset: AssetResponseDto } = event.detail;
|
||||
let temp = new Set(selectedAssets);
|
||||
let temporary = new Set(selectedAssets);
|
||||
if (selectedAssets.has(asset)) {
|
||||
temp.delete(asset);
|
||||
temporary.delete(asset);
|
||||
} else {
|
||||
temp.add(asset);
|
||||
temporary.add(asset);
|
||||
}
|
||||
|
||||
selectedAssets = temp;
|
||||
selectedAssets = temporary;
|
||||
dispatch('select', { asset, selectedAssets });
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -35,15 +35,15 @@
|
|||
|
||||
const selectAssetHandler = (event: CustomEvent) => {
|
||||
const { asset }: { asset: AssetResponseDto } = event.detail;
|
||||
let temp = new Set(selectedAssets);
|
||||
let temporary = new Set(selectedAssets);
|
||||
|
||||
if (selectedAssets.has(asset)) {
|
||||
temp.delete(asset);
|
||||
temporary.delete(asset);
|
||||
} else {
|
||||
temp.add(asset);
|
||||
temporary.add(asset);
|
||||
}
|
||||
|
||||
selectedAssets = temp;
|
||||
selectedAssets = temporary;
|
||||
};
|
||||
|
||||
const navigateAssetForward = () => {
|
||||
|
|
@ -53,8 +53,8 @@
|
|||
selectedAsset = assets[currentViewAssetIndex];
|
||||
pushState(selectedAsset.id);
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(e, 'Cannot navigate to the next asset');
|
||||
} catch (error) {
|
||||
handleError(error, 'Cannot navigate to the next asset');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -65,8 +65,8 @@
|
|||
selectedAsset = assets[currentViewAssetIndex];
|
||||
pushState(selectedAsset.id);
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(e, 'Cannot navigate to previous asset');
|
||||
} catch (error) {
|
||||
handleError(error, 'Cannot navigate to previous asset');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
let map: maplibregl.Map;
|
||||
let marker: maplibregl.Marker | null = null;
|
||||
|
||||
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||
$: style = (async () => {
|
||||
const { data } = await api.systemConfigApi.getMapStyle({
|
||||
theme: $mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT,
|
||||
|
|
@ -60,7 +61,7 @@
|
|||
}
|
||||
|
||||
const mapSource = map?.getSource('geojson') as GeoJSONSource;
|
||||
mapSource.getClusterLeaves(clusterId, 10000, 0, (error, leaves) => {
|
||||
mapSource.getClusterLeaves(clusterId, 10_000, 0, (error, leaves) => {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@
|
|||
const logOut = async () => {
|
||||
resetSavedUser();
|
||||
const { data } = await api.authenticationApi.logout();
|
||||
if (!data.redirectUri.startsWith('/')) {
|
||||
window.location.href = data.redirectUri;
|
||||
} else {
|
||||
if (data.redirectUri.startsWith('/')) {
|
||||
goto(data.redirectUri);
|
||||
} else {
|
||||
window.location.href = data.redirectUri;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
let removeNotificationTimeout: NodeJS.Timeout | undefined = undefined;
|
||||
let removeNotificationTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
onMount(() => {
|
||||
removeNotificationTimeout = setTimeout(discard, notificationInfo.timeout);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export enum NotificationType {
|
|||
}
|
||||
|
||||
export class ImmichNotification {
|
||||
id = new Date().getTime() + Math.random();
|
||||
id = Date.now() + Math.random();
|
||||
type!: NotificationType;
|
||||
message!: string;
|
||||
action!: NotificationAction;
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@
|
|||
/**
|
||||
* Usage: <div use:portal={'css selector'}> or <div use:portal={document.body}>
|
||||
*/
|
||||
export function portal(el: HTMLElement, target: HTMLElement | string = 'body') {
|
||||
let targetEl;
|
||||
export function portal(element: HTMLElement, target: HTMLElement | string = 'body') {
|
||||
let targetElement;
|
||||
async function update(newTarget: HTMLElement | string) {
|
||||
target = newTarget;
|
||||
if (typeof target === 'string') {
|
||||
targetEl = document.querySelector(target);
|
||||
if (targetEl === null) {
|
||||
targetElement = document.querySelector(target);
|
||||
if (targetElement === null) {
|
||||
await tick();
|
||||
targetEl = document.querySelector(target);
|
||||
targetElement = document.querySelector(target);
|
||||
}
|
||||
if (targetEl === null) {
|
||||
if (targetElement === null) {
|
||||
throw new Error(`No element found matching css selector: "${target}"`);
|
||||
}
|
||||
} else if (target instanceof HTMLElement) {
|
||||
targetEl = target;
|
||||
targetElement = target;
|
||||
} else {
|
||||
throw new TypeError(
|
||||
`Unknown portal target type: ${
|
||||
|
|
@ -26,13 +26,13 @@
|
|||
}. Allowed types: string (CSS selector) or HTMLElement.`,
|
||||
);
|
||||
}
|
||||
targetEl.appendChild(el);
|
||||
el.hidden = false;
|
||||
targetElement.append(element);
|
||||
element.hidden = false;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
if (el.parentNode) {
|
||||
el.parentNode.removeChild(el);
|
||||
if (element.parentNode) {
|
||||
element.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,18 +26,18 @@
|
|||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
const context = canvas.getContext('2d');
|
||||
if (!context) {
|
||||
throw new Error('Could not get canvas context.');
|
||||
}
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
context.drawImage(img, 0, 0);
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData?.data;
|
||||
if (!data) {
|
||||
throw new Error('Could not get image data.');
|
||||
}
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
if (data[i + 3] < 255) {
|
||||
for (let index = 0; index < data.length; index += 4) {
|
||||
if (data[index + 3] < 255) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -62,8 +62,8 @@
|
|||
message: 'Profile picture set.',
|
||||
timeout: 3000,
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err, 'Error setting profile picture.');
|
||||
} catch (error) {
|
||||
handleError(error, 'Error setting profile picture.');
|
||||
}
|
||||
dispatch('close');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
const calculateSegments = (buckets: AssetBucket[]) => {
|
||||
let height = 0;
|
||||
let prev: Segment;
|
||||
let previous: Segment;
|
||||
return buckets.map((bucket) => {
|
||||
const segment = new Segment();
|
||||
segment.count = bucket.assets.length;
|
||||
|
|
@ -42,13 +42,13 @@
|
|||
segment.timeGroup = bucket.bucketDate;
|
||||
segment.date = fromLocalDateTime(segment.timeGroup);
|
||||
|
||||
if (prev?.date.year !== segment.date.year && height > MIN_YEAR_LABEL_DISTANCE) {
|
||||
prev.hasLabel = true;
|
||||
if (previous?.date.year !== segment.date.year && height > MIN_YEAR_LABEL_DISTANCE) {
|
||||
previous.hasLabel = true;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
height += segment.height;
|
||||
prev = segment;
|
||||
previous = segment;
|
||||
return segment;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,14 +26,14 @@
|
|||
$savedSearchTerms = $savedSearchTerms.filter((item) => item !== value);
|
||||
saveSearchTerm(value);
|
||||
|
||||
const params = new URLSearchParams({
|
||||
const parameters = new URLSearchParams({
|
||||
q: searchValue,
|
||||
smart: smartSearch,
|
||||
});
|
||||
|
||||
showBigSearchBar = false;
|
||||
$isSearchEnabled = false;
|
||||
goto(`${AppRoute.SEARCH}?${params}`, { invalidateAll: true });
|
||||
goto(`${AppRoute.SEARCH}?${parameters}`, { invalidateAll: true });
|
||||
}
|
||||
|
||||
const clearSearchTerm = (searchTerm: string) => {
|
||||
|
|
@ -140,7 +140,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#each $savedSearchTerms as savedSearchTerm, i (i)}
|
||||
{#each $savedSearchTerms as savedSearchTerm, index (index)}
|
||||
<div
|
||||
class="flex w-full items-center justify-between text-sm text-black hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-500/10"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
}
|
||||
|
||||
showModal = true;
|
||||
} catch (err) {
|
||||
console.error('Error [VersionAnnouncementBox]:', err);
|
||||
} catch (error) {
|
||||
console.error('Error [VersionAnnouncementBox]:', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
};
|
||||
|
||||
const isExpired = (expiresAt: string) => {
|
||||
const now = new Date().getTime();
|
||||
const now = Date.now();
|
||||
const expiration = new Date(expiresAt).getTime();
|
||||
|
||||
return now > expiration;
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@
|
|||
<Icon path={mdiAndroid} size="40" />
|
||||
{:else if device.deviceOS === 'iOS' || device.deviceOS === 'Mac OS'}
|
||||
<Icon path={mdiApple} size="40" />
|
||||
{:else if device.deviceOS.indexOf('Safari') !== -1}
|
||||
{:else if device.deviceOS.includes('Safari')}
|
||||
<Icon path={mdiAppleSafari} size="40" />
|
||||
{:else if device.deviceOS.indexOf('Windows') !== -1}
|
||||
{:else if device.deviceOS.includes('Windows')}
|
||||
<Icon path={mdiMicrosoftWindows} size="40" />
|
||||
{:else if device.deviceOS === 'Linux'}
|
||||
<Icon path={mdiLinux} size="40" />
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@
|
|||
{#if otherDevices.length > 0}
|
||||
<div class="mb-6">
|
||||
<h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">OTHER DEVICES</h3>
|
||||
{#each otherDevices as device, i}
|
||||
{#each otherDevices as device, index}
|
||||
<DeviceCard {device} on:delete={() => (deleteDevice = device)} />
|
||||
{#if i !== otherDevices.length - 1}
|
||||
{#if index !== otherDevices.length - 1}
|
||||
<hr class="my-3" />
|
||||
{/if}
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@
|
|||
updateLibraryIndex = null;
|
||||
showContextMenu = false;
|
||||
|
||||
for (let i = 0; i < dropdownOpen.length; i++) {
|
||||
dropdownOpen[i] = false;
|
||||
for (let index = 0; index < dropdownOpen.length; index++) {
|
||||
dropdownOpen[index] = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -87,9 +87,9 @@
|
|||
|
||||
dropdownOpen.length = libraries.length;
|
||||
|
||||
for (let i = 0; i < libraries.length; i++) {
|
||||
await refreshStats(i);
|
||||
dropdownOpen[i] = false;
|
||||
for (let index = 0; index < libraries.length; index++) {
|
||||
await refreshStats(index);
|
||||
dropdownOpen[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,16 +22,14 @@
|
|||
|
||||
// exclude partners from the list of users available for selection
|
||||
const { data: partners } = await api.partnerApi.getPartners({ direction: 'shared-by' });
|
||||
const partnerIds = partners.map((partner) => partner.id);
|
||||
availableUsers = users.filter((user) => !partnerIds.includes(user.id));
|
||||
const partnerIds = new Set(partners.map((partner) => partner.id));
|
||||
availableUsers = users.filter((user) => !partnerIds.has(user.id));
|
||||
});
|
||||
|
||||
const selectUser = (user: UserResponseDto) => {
|
||||
if (selectedUsers.includes(user)) {
|
||||
selectedUsers = selectedUsers.filter((selectedUser) => selectedUser.id !== user.id);
|
||||
} else {
|
||||
selectedUsers = [...selectedUsers, user];
|
||||
}
|
||||
selectedUsers = selectedUsers.includes(user)
|
||||
? selectedUsers.filter((selectedUser) => selectedUser.id !== user.id)
|
||||
: [...selectedUsers, user];
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -129,11 +129,13 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
|
||||
{#each keys as key, i}
|
||||
{#each keys as key, index}
|
||||
{#key key.id}
|
||||
<tr
|
||||
class={`flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg ${
|
||||
i % 2 == 0 ? 'bg-immich-gray dark:bg-immich-dark-gray/75' : 'bg-immich-bg dark:bg-immich-dark-gray/50'
|
||||
index % 2 == 0
|
||||
? 'bg-immich-gray dark:bg-immich-dark-gray/75'
|
||||
: 'bg-immich-bg dark:bg-immich-dark-gray/50'
|
||||
}`}
|
||||
>
|
||||
<td class="w-1/3 text-ellipsis px-4 text-sm">{key.name}</td>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue