Procházet zdrojové kódy

chore(web): improve type checking (#2644)

* fix(web): use id instead of assetId

* chore(web): improve type checking

* fix test jobs

* improve type checking and resolve errors
Michel Heusschen před 2 roky
rodič
revize
9807f76aff
33 změnil soubory, kde provedl 144 přidání a 124 odebrání
  1. 5 1
      .github/workflows/test.yml
  2. 4 3
      web/package.json
  3. 2 2
      web/src/api/utils.ts
  4. 2 2
      web/src/hooks.server.ts
  5. 2 2
      web/src/lib/components/admin-page/jobs/jobs-panel.svelte
  6. 4 4
      web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte
  7. 1 1
      web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte
  8. 6 6
      web/src/lib/components/album-page/__tests__/album-card.spec.ts
  9. 3 15
      web/src/lib/components/album-page/album-card.svelte
  10. 12 0
      web/src/lib/components/album-page/album-card.ts
  11. 7 7
      web/src/lib/components/album-page/asset-selection.svelte
  12. 2 2
      web/src/lib/components/album-page/thumbnail-selection.svelte
  13. 10 12
      web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
  14. 2 2
      web/src/lib/components/forms/api-key-form.svelte
  15. 1 10
      web/src/lib/components/map-page/map-settings-modal.svelte
  16. 7 7
      web/src/lib/components/photos-page/asset-date-group.svelte
  17. 8 9
      web/src/lib/components/photos-page/asset-grid.svelte
  18. 1 1
      web/src/lib/components/photos-page/asset-select-control-bar.svelte
  19. 13 13
      web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte
  20. 1 1
      web/src/lib/components/shared-components/leaflet/marker-cluster/asset-marker-cluster.svelte
  21. 4 4
      web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
  22. 3 3
      web/src/lib/components/shared-components/upload-asset-preview.svelte
  23. 1 1
      web/src/lib/components/user-settings-page/device-card.svelte
  24. 1 1
      web/src/lib/models/asset-grid-state.ts
  25. 1 1
      web/src/lib/stores/archived-asset.store.ts
  26. 8 1
      web/src/lib/stores/preferences.store.ts
  27. 6 6
      web/src/lib/utils/file-uploader.ts
  28. 1 1
      web/src/lib/utils/handle-error.ts
  29. 2 2
      web/src/routes/(user)/albums/albums.bloc.ts
  30. 1 1
      web/src/routes/(user)/search/+page.svelte
  31. 5 2
      web/src/test-data/factories/album-factory.ts
  32. 18 0
      web/src/test-data/factories/user-factory.ts
  33. 0 1
      web/tsconfig.json

+ 5 - 1
.github/workflows/test.yml

@@ -96,7 +96,11 @@ jobs:
         if: ${{ !cancelled() }}
         if: ${{ !cancelled() }}
 
 
       - name: Run svelte checks
       - name: Run svelte checks
-        run: npm run check
+        run: npm run check:svelte
+        if: ${{ !cancelled() }}
+
+      - name: Run tsc
+        run: npm run check:typescript
         if: ${{ !cancelled() }}
         if: ${{ !cancelled() }}
 
 
       - name: Run unit tests & coverage
       - name: Run unit tests & coverage

+ 4 - 3
web/package.json

@@ -6,9 +6,10 @@
 		"build": "vite build",
 		"build": "vite build",
 		"package": "svelte-kit package",
 		"package": "svelte-kit package",
 		"preview": "vite preview",
 		"preview": "vite preview",
-		"check": "svelte-check --no-tsconfig --fail-on-warnings --ignore \"src/api/open-api\"",
-		"check:watch": "npm run check -- --watch",
-		"check:code": "npm run format && npm run lint && npm run check",
+		"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --ignore \"src/api/open-api\"",
+		"check:typescript": "tsc --noEmit",
+		"check:watch": "npm run check:svelte -- --watch",
+		"check:code": "npm run format && npm run lint && npm run check:svelte && npm run check:typescript",
 		"check:all": "npm run check:code && npm run test:cov",
 		"check:all": "npm run check:code && npm run test:cov",
 		"lint": "eslint . --max-warnings 0",
 		"lint": "eslint . --max-warnings 0",
 		"lint:fix": "npm run lint -- --fix",
 		"lint:fix": "npm run lint -- --fix",

+ 2 - 2
web/src/api/utils.ts

@@ -1,6 +1,6 @@
-import { AxiosError, AxiosPromise } from 'axios';
+import type { AxiosError, AxiosPromise } from 'axios';
 import { api } from './api';
 import { api } from './api';
-import { UserResponseDto } from './open-api';
+import type { UserResponseDto } from './open-api';
 
 
 export type ApiError = AxiosError<{ message: string }>;
 export type ApiError = AxiosError<{ message: string }>;
 
 

+ 2 - 2
web/src/hooks.server.ts

@@ -1,6 +1,6 @@
-import type { Handle, HandleServerError } from '@sveltejs/kit';
-import { AxiosError, AxiosResponse } from 'axios';
 import { env } from '$env/dynamic/public';
 import { env } from '$env/dynamic/public';
+import type { Handle, HandleServerError } from '@sveltejs/kit';
+import type { AxiosError, AxiosResponse } from 'axios';
 import { ImmichApi } from './api/api';
 import { ImmichApi } from './api/api';
 
 
 export const handle = (async ({ event, resolve }) => {
 export const handle = (async ({ event, resolve }) => {

+ 2 - 2
web/src/lib/components/admin-page/jobs/jobs-panel.svelte

@@ -3,10 +3,11 @@
 		notificationController,
 		notificationController,
 		NotificationType
 		NotificationType
 	} from '$lib/components/shared-components/notification/notification';
 	} from '$lib/components/shared-components/notification/notification';
+	import { AppRoute } from '$lib/constants';
 	import { handleError } from '$lib/utils/handle-error';
 	import { handleError } from '$lib/utils/handle-error';
 	import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api';
 	import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api';
 	import type { ComponentType } from 'svelte';
 	import type { ComponentType } from 'svelte';
-	import Icon from 'svelte-material-icons/DotsVertical.svelte';
+	import type Icon from 'svelte-material-icons/DotsVertical.svelte';
 	import FaceRecognition from 'svelte-material-icons/FaceRecognition.svelte';
 	import FaceRecognition from 'svelte-material-icons/FaceRecognition.svelte';
 	import FileJpgBox from 'svelte-material-icons/FileJpgBox.svelte';
 	import FileJpgBox from 'svelte-material-icons/FileJpgBox.svelte';
 	import FileXmlBox from 'svelte-material-icons/FileXmlBox.svelte';
 	import FileXmlBox from 'svelte-material-icons/FileXmlBox.svelte';
@@ -19,7 +20,6 @@
 	import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
 	import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
 	import JobTile from './job-tile.svelte';
 	import JobTile from './job-tile.svelte';
 	import StorageMigrationDescription from './storage-migration-description.svelte';
 	import StorageMigrationDescription from './storage-migration-description.svelte';
-	import { AppRoute } from '$lib/constants';
 
 
 	export let jobs: AllJobStatusResponseDto;
 	export let jobs: AllJobStatusResponseDto;
 
 

+ 4 - 4
web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte

@@ -1,11 +1,11 @@
 <script lang="ts">
 <script lang="ts">
-	import { ServerStatsResponseDto } from '@api';
+	import { locale } from '$lib/stores/preferences.store';
+	import type { ServerStatsResponseDto } from '@api';
 	import CameraIris from 'svelte-material-icons/CameraIris.svelte';
 	import CameraIris from 'svelte-material-icons/CameraIris.svelte';
-	import PlayCircle from 'svelte-material-icons/PlayCircle.svelte';
 	import Memory from 'svelte-material-icons/Memory.svelte';
 	import Memory from 'svelte-material-icons/Memory.svelte';
-	import StatsCard from './stats-card.svelte';
+	import PlayCircle from 'svelte-material-icons/PlayCircle.svelte';
 	import { asByteUnitString, getBytesWithUnit } from '../../../utils/byte-units';
 	import { asByteUnitString, getBytesWithUnit } from '../../../utils/byte-units';
-	import { locale } from '$lib/stores/preferences.store';
+	import StatsCard from './stats-card.svelte';
 
 
 	export let stats: ServerStatsResponseDto = {
 	export let stats: ServerStatsResponseDto = {
 		photos: 0,
 		photos: 0,

+ 1 - 1
web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte

@@ -1,5 +1,5 @@
 <script lang="ts">
 <script lang="ts">
-	import { SystemConfigTemplateStorageOptionDto } from '@api';
+	import type { SystemConfigTemplateStorageOptionDto } from '@api';
 	import * as luxon from 'luxon';
 	import * as luxon from 'luxon';
 
 
 	export let options: SystemConfigTemplateStorageOptionDto;
 	export let options: SystemConfigTemplateStorageOptionDto;

+ 6 - 6
web/src/lib/components/album-page/__tests__/album-card.spec.ts

@@ -37,7 +37,7 @@ describe('AlbumCard component', () => {
 	])(
 	])(
 		'shows album data without thumbnail with count $count - shared: $shared',
 		'shows album data without thumbnail with count $count - shared: $shared',
 		async ({ album, count, shared }) => {
 		async ({ album, count, shared }) => {
-			sut = render(AlbumCard, { album });
+			sut = render(AlbumCard, { album, user: album.owner });
 
 
 			const albumImgElement = sut.getByTestId('album-image');
 			const albumImgElement = sut.getByTestId('album-image');
 			const albumNameElement = sut.getByTestId('album-name');
 			const albumNameElement = sut.getByTestId('album-name');
@@ -58,10 +58,10 @@ describe('AlbumCard component', () => {
 	);
 	);
 
 
 	it('shows album data and and loads the thumbnail image when available', async () => {
 	it('shows album data and and loads the thumbnail image when available', async () => {
-		const thumbnailBlob = new Blob();
+		const thumbnailFile = new File([new Blob()], 'fileThumbnail');
 		const thumbnailUrl = 'blob:thumbnailUrlOne';
 		const thumbnailUrl = 'blob:thumbnailUrlOne';
 		apiMock.assetApi.getAssetThumbnail.mockResolvedValue({
 		apiMock.assetApi.getAssetThumbnail.mockResolvedValue({
-			data: thumbnailBlob,
+			data: thumbnailFile,
 			config: {},
 			config: {},
 			headers: {},
 			headers: {},
 			status: 200,
 			status: 200,
@@ -74,7 +74,7 @@ describe('AlbumCard component', () => {
 			shared: false,
 			shared: false,
 			albumName: 'some album name'
 			albumName: 'some album name'
 		});
 		});
-		sut = render(AlbumCard, { album });
+		sut = render(AlbumCard, { album, user: album.owner });
 
 
 		const albumImgElement = sut.getByTestId('album-image');
 		const albumImgElement = sut.getByTestId('album-image');
 		const albumNameElement = sut.getByTestId('album-name');
 		const albumNameElement = sut.getByTestId('album-name');
@@ -92,7 +92,7 @@ describe('AlbumCard component', () => {
 			},
 			},
 			{ responseType: 'blob' }
 			{ responseType: 'blob' }
 		);
 		);
-		expect(createObjectURLMock).toHaveBeenCalledWith(thumbnailBlob);
+		expect(createObjectURLMock).toHaveBeenCalledWith(thumbnailFile);
 
 
 		expect(albumNameElement).toHaveTextContent('some album name');
 		expect(albumNameElement).toHaveTextContent('some album name');
 		expect(albumDetailsElement).toHaveTextContent('0 items');
 		expect(albumDetailsElement).toHaveTextContent('0 items');
@@ -102,7 +102,7 @@ describe('AlbumCard component', () => {
 		const album = Object.freeze(albumFactory.build({ albumThumbnailAssetId: null }));
 		const album = Object.freeze(albumFactory.build({ albumThumbnailAssetId: null }));
 
 
 		beforeEach(async () => {
 		beforeEach(async () => {
-			sut = render(AlbumCard, { album });
+			sut = render(AlbumCard, { album, user: album.owner });
 
 
 			const albumImgElement = sut.getByTestId('album-image');
 			const albumImgElement = sut.getByTestId('album-image');
 			await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));
 			await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));

+ 3 - 15
web/src/lib/components/album-page/album-card.svelte

@@ -1,23 +1,11 @@
-<script lang="ts" context="module">
-	type OnShowContextMenu = {
-		showalbumcontextmenu: OnShowContextMenuDetail;
-	};
-
-	type OnClick = {
-		click: OnClickDetail;
-	};
-
-	export type OnShowContextMenuDetail = { x: number; y: number };
-	export type OnClickDetail = AlbumResponseDto;
-</script>
-
 <script lang="ts">
 <script lang="ts">
+	import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
+	import { locale } from '$lib/stores/preferences.store';
 	import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 	import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 	import { createEventDispatcher, onMount } from 'svelte';
 	import { createEventDispatcher, onMount } from 'svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
-	import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
-	import { locale } from '$lib/stores/preferences.store';
 	import IconButton from '../elements/buttons/icon-button.svelte';
 	import IconButton from '../elements/buttons/icon-button.svelte';
+	import type { OnClick, OnShowContextMenu } from './album-card';
 
 
 	export let album: AlbumResponseDto;
 	export let album: AlbumResponseDto;
 	export let isSharingView = false;
 	export let isSharingView = false;

+ 12 - 0
web/src/lib/components/album-page/album-card.ts

@@ -0,0 +1,12 @@
+import type { AlbumResponseDto } from '@api';
+
+export type OnShowContextMenu = {
+	showalbumcontextmenu: OnShowContextMenuDetail;
+};
+
+export type OnClick = {
+	click: OnClickDetail;
+};
+
+export type OnShowContextMenuDetail = { x: number; y: number };
+export type OnClickDetail = AlbumResponseDto;

+ 7 - 7
web/src/lib/components/album-page/asset-selection.svelte

@@ -1,18 +1,18 @@
 <script lang="ts">
 <script lang="ts">
-	import { createEventDispatcher, onMount } from 'svelte';
-	import { quintOut } from 'svelte/easing';
-	import { fly } from 'svelte/transition';
-	import { AssetResponseDto } from '@api';
-	import { openFileUploadDialog } from '$lib/utils/file-uploader';
-	import ControlAppBar from '../shared-components/control-app-bar.svelte';
-	import AssetGrid from '../photos-page/asset-grid.svelte';
 	import {
 	import {
 		assetInteractionStore,
 		assetInteractionStore,
 		assetsInAlbumStoreState,
 		assetsInAlbumStoreState,
 		selectedAssets
 		selectedAssets
 	} from '$lib/stores/asset-interaction.store';
 	} from '$lib/stores/asset-interaction.store';
 	import { locale } from '$lib/stores/preferences.store';
 	import { locale } from '$lib/stores/preferences.store';
+	import { openFileUploadDialog } from '$lib/utils/file-uploader';
+	import type { AssetResponseDto } from '@api';
+	import { createEventDispatcher, onMount } from 'svelte';
+	import { quintOut } from 'svelte/easing';
+	import { fly } from 'svelte/transition';
 	import Button from '../elements/buttons/button.svelte';
 	import Button from '../elements/buttons/button.svelte';
+	import AssetGrid from '../photos-page/asset-grid.svelte';
+	import ControlAppBar from '../shared-components/control-app-bar.svelte';
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 

+ 2 - 2
web/src/lib/components/album-page/thumbnail-selection.svelte

@@ -1,11 +1,11 @@
 <script lang="ts">
 <script lang="ts">
-	import { AlbumResponseDto, AssetResponseDto } from '@api';
+	import type { AlbumResponseDto, AssetResponseDto } from '@api';
 	import { createEventDispatcher } from 'svelte';
 	import { createEventDispatcher } from 'svelte';
 	import { quintOut } from 'svelte/easing';
 	import { quintOut } from 'svelte/easing';
 	import { fly } from 'svelte/transition';
 	import { fly } from 'svelte/transition';
 	import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
 	import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
-	import ControlAppBar from '../shared-components/control-app-bar.svelte';
 	import Button from '../elements/buttons/button.svelte';
 	import Button from '../elements/buttons/button.svelte';
+	import ControlAppBar from '../shared-components/control-app-bar.svelte';
 
 
 	export let album: AlbumResponseDto;
 	export let album: AlbumResponseDto;
 
 

+ 10 - 12
web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte

@@ -1,23 +1,21 @@
 <script lang="ts">
 <script lang="ts">
-	import { createEventDispatcher } from 'svelte';
-
+	import { page } from '$app/stores';
 	import { clickOutside } from '$lib/utils/click-outside';
 	import { clickOutside } from '$lib/utils/click-outside';
+	import type { AssetResponseDto } from '@api';
+	import { createEventDispatcher } from 'svelte';
 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 	import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
 	import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
-	import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
-	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
+	import ContentCopy from 'svelte-material-icons/ContentCopy.svelte';
 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
-	import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
-	import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
-	import MenuOption from '../shared-components/context-menu/menu-option.svelte';
+	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 	import Heart from 'svelte-material-icons/Heart.svelte';
 	import Heart from 'svelte-material-icons/Heart.svelte';
 	import HeartOutline from 'svelte-material-icons/HeartOutline.svelte';
 	import HeartOutline from 'svelte-material-icons/HeartOutline.svelte';
-	import ContentCopy from 'svelte-material-icons/ContentCopy.svelte';
-	import MotionPlayOutline from 'svelte-material-icons/MotionPlayOutline.svelte';
+	import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
 	import MotionPauseOutline from 'svelte-material-icons/MotionPauseOutline.svelte';
 	import MotionPauseOutline from 'svelte-material-icons/MotionPauseOutline.svelte';
-
-	import { page } from '$app/stores';
-	import { AssetResponseDto } from '../../../api';
+	import MotionPlayOutline from 'svelte-material-icons/MotionPlayOutline.svelte';
+	import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
+	import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
+	import MenuOption from '../shared-components/context-menu/menu-option.svelte';
 
 
 	export let asset: AssetResponseDto;
 	export let asset: AssetResponseDto;
 	export let showCopyButton: boolean;
 	export let showCopyButton: boolean;

+ 2 - 2
web/src/lib/components/forms/api-key-form.svelte

@@ -1,9 +1,9 @@
 <script lang="ts">
 <script lang="ts">
-	import { APIKeyResponseDto } from '@api';
+	import type { APIKeyResponseDto } from '@api';
 	import { createEventDispatcher } from 'svelte';
 	import { createEventDispatcher } from 'svelte';
 	import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
 	import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
-	import FullScreenModal from '../shared-components/full-screen-modal.svelte';
 	import Button from '../elements/buttons/button.svelte';
 	import Button from '../elements/buttons/button.svelte';
+	import FullScreenModal from '../shared-components/full-screen-modal.svelte';
 
 
 	export let apiKey: Partial<APIKeyResponseDto>;
 	export let apiKey: Partial<APIKeyResponseDto>;
 	export let title = 'API Key';
 	export let title = 'API Key';

+ 1 - 10
web/src/lib/components/map-page/map-settings-modal.svelte

@@ -1,15 +1,6 @@
-<script lang="ts" context="module">
-	export interface MapSettings {
-		allowDarkMode: boolean;
-		onlyFavorites: boolean;
-		relativeDate: string;
-		dateAfter: string;
-		dateBefore: string;
-	}
-</script>
-
 <script lang="ts">
 <script lang="ts">
 	import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 	import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
+	import type { MapSettings } from '$lib/stores/preferences.store';
 	import { Duration } from 'luxon';
 	import { Duration } from 'luxon';
 	import { createEventDispatcher } from 'svelte';
 	import { createEventDispatcher } from 'svelte';
 	import { fly } from 'svelte/transition';
 	import { fly } from 'svelte/transition';

+ 7 - 7
web/src/lib/components/photos-page/asset-date-group.svelte

@@ -1,10 +1,4 @@
 <script lang="ts">
 <script lang="ts">
-	import { assetStore } from '$lib/stores/assets.store';
-	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
-	import CircleOutline from 'svelte-material-icons/CircleOutline.svelte';
-	import { fly } from 'svelte/transition';
-	import { AssetResponseDto } from '@api';
-	import lodash from 'lodash-es';
 	import {
 	import {
 		assetInteractionStore,
 		assetInteractionStore,
 		assetsInAlbumStoreState,
 		assetsInAlbumStoreState,
@@ -12,9 +6,15 @@
 		selectedAssets,
 		selectedAssets,
 		selectedGroup
 		selectedGroup
 	} from '$lib/stores/asset-interaction.store';
 	} from '$lib/stores/asset-interaction.store';
+	import { assetStore } from '$lib/stores/assets.store';
 	import { locale } from '$lib/stores/preferences.store';
 	import { locale } from '$lib/stores/preferences.store';
-	import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
+	import type { AssetResponseDto } from '@api';
+	import lodash from 'lodash-es';
+	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
+	import CircleOutline from 'svelte-material-icons/CircleOutline.svelte';
 	import { flip } from 'svelte/animate';
 	import { flip } from 'svelte/animate';
+	import { fly } from 'svelte/transition';
+	import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
 
 
 	export let assets: AssetResponseDto[];
 	export let assets: AssetResponseDto[];
 	export let bucketDate: string;
 	export let bucketDate: string;

+ 8 - 9
web/src/lib/components/photos-page/asset-grid.svelte

@@ -1,22 +1,21 @@
 <script lang="ts">
 <script lang="ts">
-	import { onDestroy, onMount } from 'svelte';
-
-	import { UserResponseDto } from '@api';
-	import IntersectionObserver from '../asset-viewer/intersection-observer.svelte';
-	import { assetGridState, assetStore, loadingBucketState } from '$lib/stores/assets.store';
-	import { api, AssetCountByTimeBucketResponseDto, AssetResponseDto, TimeGroupEnum } from '@api';
-	import AssetDateGroup from './asset-date-group.svelte';
-	import Portal from '../shared-components/portal/portal.svelte';
-	import AssetViewer from '../asset-viewer/asset-viewer.svelte';
 	import {
 	import {
 		assetInteractionStore,
 		assetInteractionStore,
 		isViewingAssetStoreState,
 		isViewingAssetStoreState,
 		viewingAssetStoreState
 		viewingAssetStoreState
 	} from '$lib/stores/asset-interaction.store';
 	} from '$lib/stores/asset-interaction.store';
+	import { assetGridState, assetStore, loadingBucketState } from '$lib/stores/assets.store';
+	import type { UserResponseDto } from '@api';
+	import { AssetCountByTimeBucketResponseDto, AssetResponseDto, TimeGroupEnum, api } from '@api';
+	import { onDestroy, onMount } from 'svelte';
+	import AssetViewer from '../asset-viewer/asset-viewer.svelte';
+	import IntersectionObserver from '../asset-viewer/intersection-observer.svelte';
+	import Portal from '../shared-components/portal/portal.svelte';
 	import Scrollbar, {
 	import Scrollbar, {
 		OnScrollbarClickDetail,
 		OnScrollbarClickDetail,
 		OnScrollbarDragDetail
 		OnScrollbarDragDetail
 	} from '../shared-components/scrollbar/scrollbar.svelte';
 	} from '../shared-components/scrollbar/scrollbar.svelte';
+	import AssetDateGroup from './asset-date-group.svelte';
 
 
 	export let user: UserResponseDto | undefined = undefined;
 	export let user: UserResponseDto | undefined = undefined;
 	export let isAlbumSelectionMode = false;
 	export let isAlbumSelectionMode = false;

+ 1 - 1
web/src/lib/components/photos-page/asset-select-control-bar.svelte

@@ -17,7 +17,7 @@
 
 
 <script lang="ts">
 <script lang="ts">
 	import { locale } from '$lib/stores/preferences.store';
 	import { locale } from '$lib/stores/preferences.store';
-	import { AssetResponseDto } from '@api';
+	import type { AssetResponseDto } from '@api';
 	import Close from 'svelte-material-icons/Close.svelte';
 	import Close from 'svelte-material-icons/Close.svelte';
 	import ControlAppBar from '../shared-components/control-app-bar.svelte';
 	import ControlAppBar from '../shared-components/control-app-bar.svelte';
 
 

+ 13 - 13
web/src/lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte

@@ -1,23 +1,23 @@
 <script lang="ts">
 <script lang="ts">
-	import { createEventDispatcher, onMount } from 'svelte';
-	import BaseModal from '../base-modal.svelte';
-	import Link from 'svelte-material-icons/Link.svelte';
+	import SettingInputField, {
+		SettingInputFieldType
+	} from '$lib/components/admin-page/settings/setting-input-field.svelte';
+	import SettingSwitch from '$lib/components/admin-page/settings/setting-switch.svelte';
+	import Button from '$lib/components/elements/buttons/button.svelte';
+	import { handleError } from '$lib/utils/handle-error';
 	import {
 	import {
 		AlbumResponseDto,
 		AlbumResponseDto,
-		api,
 		AssetResponseDto,
 		AssetResponseDto,
 		SharedLinkResponseDto,
 		SharedLinkResponseDto,
-		SharedLinkType
+		SharedLinkType,
+		api
 	} from '@api';
 	} from '@api';
-	import { notificationController, NotificationType } from '../notification/notification';
-	import { ImmichDropDownOption } from '../dropdown-button.svelte';
-	import SettingSwitch from '$lib/components/admin-page/settings/setting-switch.svelte';
+	import { createEventDispatcher, onMount } from 'svelte';
+	import Link from 'svelte-material-icons/Link.svelte';
+	import BaseModal from '../base-modal.svelte';
+	import type { ImmichDropDownOption } from '../dropdown-button.svelte';
 	import DropdownButton from '../dropdown-button.svelte';
 	import DropdownButton from '../dropdown-button.svelte';
-	import SettingInputField, {
-		SettingInputFieldType
-	} from '$lib/components/admin-page/settings/setting-input-field.svelte';
-	import { handleError } from '$lib/utils/handle-error';
-	import Button from '$lib/components/elements/buttons/button.svelte';
+	import { NotificationType, notificationController } from '../notification/notification';
 
 
 	export let shareType: SharedLinkType;
 	export let shareType: SharedLinkType;
 	export let sharedAssets: AssetResponseDto[] = [];
 	export let sharedAssets: AssetResponseDto[] = [];

+ 1 - 1
web/src/lib/components/shared-components/leaflet/marker-cluster/asset-marker-cluster.svelte

@@ -10,7 +10,7 @@
 </script>
 </script>
 
 
 <script lang="ts">
 <script lang="ts">
-	import { MapMarkerResponseDto } from '@api';
+	import type { MapMarkerResponseDto } from '@api';
 	import { DivIcon, LeafletEvent, LeafletMouseEvent, MarkerCluster, Point } from 'leaflet';
 	import { DivIcon, LeafletEvent, LeafletMouseEvent, MarkerCluster, Point } from 'leaflet';
 	import 'leaflet.markercluster';
 	import 'leaflet.markercluster';
 	import { createEventDispatcher, onDestroy, onMount } from 'svelte';
 	import { createEventDispatcher, onDestroy, onMount } from 'svelte';

+ 4 - 4
web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte

@@ -1,12 +1,12 @@
 <script lang="ts">
 <script lang="ts">
-	import { UserResponseDto } from '@api';
+	import Button from '$lib/components/elements/buttons/button.svelte';
+	import { AppRoute } from '$lib/constants';
+	import type { UserResponseDto } from '@api';
 	import { createEventDispatcher } from 'svelte';
 	import { createEventDispatcher } from 'svelte';
-	import { fade } from 'svelte/transition';
 	import Cog from 'svelte-material-icons/Cog.svelte';
 	import Cog from 'svelte-material-icons/Cog.svelte';
 	import Logout from 'svelte-material-icons/Logout.svelte';
 	import Logout from 'svelte-material-icons/Logout.svelte';
-	import Button from '$lib/components/elements/buttons/button.svelte';
+	import { fade } from 'svelte/transition';
 	import UserAvatar from '../user-avatar.svelte';
 	import UserAvatar from '../user-avatar.svelte';
-	import { AppRoute } from '$lib/constants';
 
 
 	export let user: UserResponseDto;
 	export let user: UserResponseDto;
 
 

+ 3 - 3
web/src/lib/components/shared-components/upload-asset-preview.svelte

@@ -1,9 +1,9 @@
 <script lang="ts">
 <script lang="ts">
-	import { fade } from 'svelte/transition';
+	import type { UploadAsset } from '$lib/models/upload-asset';
+	import { locale } from '$lib/stores/preferences.store';
 	import { asByteUnitString } from '$lib/utils/byte-units';
 	import { asByteUnitString } from '$lib/utils/byte-units';
-	import { UploadAsset } from '$lib/models/upload-asset';
+	import { fade } from 'svelte/transition';
 	import ImmichLogo from './immich-logo.svelte';
 	import ImmichLogo from './immich-logo.svelte';
-	import { locale } from '$lib/stores/preferences.store';
 
 
 	export let uploadAsset: UploadAsset;
 	export let uploadAsset: UploadAsset;
 
 

+ 1 - 1
web/src/lib/components/user-settings-page/device-card.svelte

@@ -1,6 +1,6 @@
 <script lang="ts">
 <script lang="ts">
 	import { locale } from '$lib/stores/preferences.store';
 	import { locale } from '$lib/stores/preferences.store';
-	import { AuthDeviceResponseDto } from '@api';
+	import type { AuthDeviceResponseDto } from '@api';
 	import { DateTime, ToRelativeCalendarOptions } from 'luxon';
 	import { DateTime, ToRelativeCalendarOptions } from 'luxon';
 	import { createEventDispatcher } from 'svelte';
 	import { createEventDispatcher } from 'svelte';
 	import Android from 'svelte-material-icons/Android.svelte';
 	import Android from 'svelte-material-icons/Android.svelte';

+ 1 - 1
web/src/lib/models/asset-grid-state.ts

@@ -1,4 +1,4 @@
-import { AssetResponseDto } from '@api';
+import type { AssetResponseDto } from '@api';
 
 
 export class AssetBucket {
 export class AssetBucket {
 	/**
 	/**

+ 1 - 1
web/src/lib/stores/archived-asset.store.ts

@@ -1,4 +1,4 @@
-import { AssetResponseDto } from '@api';
+import type { AssetResponseDto } from '@api';
 import { writable } from 'svelte/store';
 import { writable } from 'svelte/store';
 
 
 export const archivedAsset = writable<AssetResponseDto[]>([]);
 export const archivedAsset = writable<AssetResponseDto[]>([]);

+ 8 - 1
web/src/lib/stores/preferences.store.ts

@@ -1,5 +1,4 @@
 import { browser } from '$app/environment';
 import { browser } from '$app/environment';
-import { MapSettings } from '$lib/components/map-page/map-settings-modal.svelte';
 import { persisted } from 'svelte-local-storage-store';
 import { persisted } from 'svelte-local-storage-store';
 
 
 const initialTheme =
 const initialTheme =
@@ -21,6 +20,14 @@ export const locale = persisted<string | undefined>('locale', undefined, {
 	}
 	}
 });
 });
 
 
+export interface MapSettings {
+	allowDarkMode: boolean;
+	onlyFavorites: boolean;
+	relativeDate: string;
+	dateAfter: string;
+	dateBefore: string;
+}
+
 export const mapSettings = persisted<MapSettings>('map-settings', {
 export const mapSettings = persisted<MapSettings>('map-settings', {
 	allowDarkMode: true,
 	allowDarkMode: true,
 	onlyFavorites: false,
 	onlyFavorites: false,

+ 6 - 6
web/src/lib/utils/file-uploader.ts

@@ -1,13 +1,13 @@
+import { uploadAssetsStore } from '$lib/stores/upload';
+import { addAssetsToAlbum, getFileMimeType, getFilenameExtension } from '$lib/utils/asset-utils';
+import type { AssetFileUploadResponseDto } from '@api';
+import axios from 'axios';
+import { combineLatestAll, filter, firstValueFrom, from, mergeMap, of } from 'rxjs';
+import type { UploadAsset } from '../models/upload-asset';
 import {
 import {
 	notificationController,
 	notificationController,
 	NotificationType
 	NotificationType
 } from './../components/shared-components/notification/notification';
 } from './../components/shared-components/notification/notification';
-import { uploadAssetsStore } from '$lib/stores/upload';
-import type { UploadAsset } from '../models/upload-asset';
-import { AssetFileUploadResponseDto } from '@api';
-import { addAssetsToAlbum, getFileMimeType, getFilenameExtension } from '$lib/utils/asset-utils';
-import { mergeMap, filter, firstValueFrom, from, of, combineLatestAll } from 'rxjs';
-import axios from 'axios';
 
 
 export const openFileUploadDialog = async (
 export const openFileUploadDialog = async (
 	albumId: string | undefined = undefined,
 	albumId: string | undefined = undefined,

+ 1 - 1
web/src/lib/utils/handle-error.ts

@@ -1,4 +1,4 @@
-import { ApiError } from '../../api';
+import type { ApiError } from '@api';
 import {
 import {
 	notificationController,
 	notificationController,
 	NotificationType
 	NotificationType

+ 2 - 2
web/src/routes/(user)/albums/albums.bloc.ts

@@ -1,10 +1,10 @@
+import type { OnShowContextMenuDetail } from '$lib/components/album-page/album-card';
 import {
 import {
 	notificationController,
 	notificationController,
 	NotificationType
 	NotificationType
 } from '$lib/components/shared-components/notification/notification';
 } from '$lib/components/shared-components/notification/notification';
 import { AlbumResponseDto, api } from '@api';
 import { AlbumResponseDto, api } from '@api';
-import { OnShowContextMenuDetail } from '$lib/components/album-page/album-card.svelte';
-import { writable, derived, get } from 'svelte/store';
+import { derived, get, writable } from 'svelte/store';
 
 
 type AlbumsProps = { albums: AlbumResponseDto[] };
 type AlbumsProps = { albums: AlbumResponseDto[] };
 
 

+ 1 - 1
web/src/routes/(user)/search/+page.svelte

@@ -12,7 +12,7 @@
 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 	import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
 	import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
 	import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte';
 	import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte';
-	import { AssetResponseDto } from '@api';
+	import type { AssetResponseDto } from '@api';
 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 	import ImageOffOutline from 'svelte-material-icons/ImageOffOutline.svelte';
 	import ImageOffOutline from 'svelte-material-icons/ImageOffOutline.svelte';

+ 5 - 2
web/src/test-data/factories/album-factory.ts

@@ -1,6 +1,7 @@
-import { AlbumResponseDto } from '@api';
-import { Sync } from 'factory.ts';
+import type { AlbumResponseDto } from '@api';
 import { faker } from '@faker-js/faker';
 import { faker } from '@faker-js/faker';
+import { Sync } from 'factory.ts';
+import { userFactory } from './user-factory';
 
 
 export const albumFactory = Sync.makeFactory<AlbumResponseDto>({
 export const albumFactory = Sync.makeFactory<AlbumResponseDto>({
 	albumName: Sync.each(() => faker.commerce.product()),
 	albumName: Sync.each(() => faker.commerce.product()),
@@ -8,8 +9,10 @@ export const albumFactory = Sync.makeFactory<AlbumResponseDto>({
 	assetCount: Sync.each((i) => i % 5),
 	assetCount: Sync.each((i) => i % 5),
 	assets: [],
 	assets: [],
 	createdAt: Sync.each(() => faker.date.past().toISOString()),
 	createdAt: Sync.each(() => faker.date.past().toISOString()),
+	updatedAt: Sync.each(() => faker.date.past().toISOString()),
 	id: Sync.each(() => faker.datatype.uuid()),
 	id: Sync.each(() => faker.datatype.uuid()),
 	ownerId: Sync.each(() => faker.datatype.uuid()),
 	ownerId: Sync.each(() => faker.datatype.uuid()),
+	owner: userFactory.build(),
 	shared: false,
 	shared: false,
 	sharedUsers: []
 	sharedUsers: []
 });
 });

+ 18 - 0
web/src/test-data/factories/user-factory.ts

@@ -0,0 +1,18 @@
+import type { UserResponseDto } from '@api';
+import { faker } from '@faker-js/faker';
+import { Sync } from 'factory.ts';
+
+export const userFactory = Sync.makeFactory<UserResponseDto>({
+	id: Sync.each(() => faker.datatype.uuid()),
+	email: Sync.each(() => faker.internet.email()),
+	firstName: Sync.each(() => faker.name.firstName()),
+	lastName: Sync.each(() => faker.name.lastName()),
+	storageLabel: Sync.each(() => faker.random.alphaNumeric()),
+	profileImagePath: '',
+	shouldChangePassword: Sync.each(() => faker.datatype.boolean()),
+	isAdmin: true,
+	createdAt: Sync.each(() => faker.date.past().toISOString()),
+	deletedAt: null,
+	updatedAt: Sync.each(() => faker.date.past().toISOString()),
+	oauthId: ''
+});

+ 0 - 1
web/tsconfig.json

@@ -16,7 +16,6 @@
     "sourceMap": true,
     "sourceMap": true,
     "strict": true,
     "strict": true,
     "target": "es2020",
     "target": "es2020",
-    "importsNotUsedAsValues": "preserve",
     "preserveValueImports": false,
     "preserveValueImports": false,
     "paths": {
     "paths": {
       "$lib": [
       "$lib": [