Browse Source

refactor(web): use ImmichApi to create urls (#2435)

Michel Heusschen 2 years ago
parent
commit
4524aa0d06

+ 15 - 4
web/src/api/api.ts

@@ -2,6 +2,7 @@ import {
 	AlbumApi,
 	APIKeyApi,
 	AssetApi,
+	AssetApiFp,
 	AuthenticationApi,
 	Configuration,
 	ConfigurationParameters,
@@ -11,11 +12,12 @@ import {
 	ServerInfoApi,
 	ShareApi,
 	SystemConfigApi,
-	ThumbnailFormat,
-	UserApi
+	UserApi,
+	UserApiFp
 } from './open-api';
 import { BASE_PATH } from './open-api/base';
 import { DUMMY_BASE_URL, toPathString } from './open-api/common';
+import type { ApiParams } from './types';
 
 export class ImmichApi {
 	public userApi: UserApi;
@@ -75,15 +77,24 @@ export class ImmichApi {
 		this.config.basePath = baseUrl;
 	}
 
-	public getAssetFileUrl(assetId: string, isThumb?: boolean, isWeb?: boolean, key?: string) {
+	public getAssetFileUrl(
+		...[assetId, isThumb, isWeb, key]: ApiParams<typeof AssetApiFp, 'serveFile'>
+	) {
 		const path = `/asset/file/${assetId}`;
 		return this.createUrl(path, { isThumb, isWeb, key });
 	}
 
-	public getAssetThumbnailUrl(assetId: string, format?: ThumbnailFormat, key?: string) {
+	public getAssetThumbnailUrl(
+		...[assetId, format, key]: ApiParams<typeof AssetApiFp, 'getAssetThumbnail'>
+	) {
 		const path = `/asset/thumbnail/${assetId}`;
 		return this.createUrl(path, { format, key });
 	}
+
+	public getProfileImageUrl(...[userId]: ApiParams<typeof UserApiFp, 'getProfileImage'>) {
+		const path = `/user/profile-image/${userId}`;
+		return this.createUrl(path);
+	}
 }
 
 export const api = new ImmichApi({ basePath: '/api' });

+ 12 - 0
web/src/api/types.ts

@@ -0,0 +1,12 @@
+import type { Configuration } from './open-api';
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+export type ApiFp = (configuration: Configuration) => Record<any, (...args: any) => any>;
+
+export type OmitLast<T extends readonly unknown[]> = T extends readonly [...infer U, any?]
+	? U
+	: [...T];
+
+export type ApiParams<F extends ApiFp, K extends keyof ReturnType<F>> = OmitLast<
+	Parameters<ReturnType<F>[K]>
+>;

+ 0 - 13
web/src/api/utils.ts

@@ -2,19 +2,6 @@ import { AxiosError, AxiosPromise } from 'axios';
 import { api } from './api';
 import { UserResponseDto } from './open-api';
 
-const _basePath = '/api';
-
-export function getFileUrl(assetId: string, isThumb?: boolean, isWeb?: boolean, key?: string) {
-	const urlObj = new URL(`${window.location.origin}${_basePath}/asset/file/${assetId}`);
-
-	if (isThumb !== undefined && isThumb !== null)
-		urlObj.searchParams.append('isThumb', `${isThumb}`);
-	if (isWeb !== undefined && isWeb !== null) urlObj.searchParams.append('isWeb', `${isWeb}`);
-
-	if (key !== undefined && key !== null) urlObj.searchParams.append('key', key);
-	return urlObj.href;
-}
-
 export type ApiError = AxiosError<{ message: string }>;
 
 export const oauth = {

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

@@ -79,11 +79,6 @@ describe('AlbumCard component', () => {
 		const albumImgElement = sut.getByTestId('album-image');
 		const albumNameElement = sut.getByTestId('album-name');
 		const albumDetailsElement = sut.getByTestId('album-details');
-		// TODO: is this expected?
-		expect(albumImgElement).toHaveAttribute(
-			'src',
-			'/api/asset/thumbnail/thumbnailIdOne?format=WEBP'
-		);
 		expect(albumImgElement).toHaveAttribute('alt', album.id);
 
 		await waitFor(() => expect(albumImgElement).toHaveAttribute('src', thumbnailUrl));

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

@@ -23,10 +23,9 @@
 	export let isSharingView = false;
 	export let user: UserResponseDto;
 
-	let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
-	if (!album.albumThumbnailAssetId) {
-		imageData = noThumbnailUrl;
-	}
+	$: imageData = album.albumThumbnailAssetId
+		? api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)
+		: noThumbnailUrl;
 
 	const dispatchClick = createEventDispatcher<OnClick>();
 	const dispatchShowContextMenu = createEventDispatcher<OnShowContextMenu>();

+ 3 - 2
web/src/lib/components/asset-viewer/album-list-item.svelte

@@ -1,5 +1,5 @@
 <script lang="ts">
-	import { AlbumResponseDto, ThumbnailFormat } from '@api';
+	import { AlbumResponseDto, ThumbnailFormat, api } from '@api';
 	import { createEventDispatcher } from 'svelte';
 
 	const dispatcher = createEventDispatcher();
@@ -29,7 +29,8 @@
 >
 	<div class="h-12 w-12">
 		<img
-			src={`/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`}
+			src={album.albumThumbnailAssetId &&
+				api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
 			alt={album.albumName}
 			class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`}
 			data-testid="album-image"

+ 3 - 2
web/src/lib/components/asset-viewer/detail-panel.svelte

@@ -5,7 +5,7 @@
 	import CameraIris from 'svelte-material-icons/CameraIris.svelte';
 	import MapMarkerOutline from 'svelte-material-icons/MapMarkerOutline.svelte';
 	import { createEventDispatcher } from 'svelte';
-	import { AssetResponseDto, AlbumResponseDto, api } from '@api';
+	import { AssetResponseDto, AlbumResponseDto, api, ThumbnailFormat } from '@api';
 	import { asByteUnitString } from '../../utils/byte-units';
 	import { locale } from '$lib/stores/preferences.store';
 	import { DateTime } from 'luxon';
@@ -241,7 +241,8 @@
 						<img
 							alt={album.albumName}
 							class="w-[50px] h-[50px] object-cover rounded"
-							src={`/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=JPEG`}
+							src={album.albumThumbnailAssetId &&
+								api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
 							draggable="false"
 						/>
 					</div>

+ 3 - 3
web/src/lib/components/asset-viewer/video-viewer.svelte

@@ -1,11 +1,11 @@
 <script lang="ts">
+	import { api } from '@api';
 	import { fade } from 'svelte/transition';
 	import { createEventDispatcher } from 'svelte';
 	import LoadingSpinner from '../shared-components/loading-spinner.svelte';
-	import { getFileUrl } from '@api';
 
 	export let assetId: string;
-	export let publicSharedKey = '';
+	export let publicSharedKey: string | undefined = undefined;
 
 	let isVideoLoading = true;
 	const dispatch = createEventDispatcher();
@@ -31,7 +31,7 @@
 		on:canplay={handleCanPlay}
 		on:ended={() => dispatch('onVideoEnded')}
 	>
-		<source src={getFileUrl(assetId, false, true, publicSharedKey)} type="video/mp4" />
+		<source src={api.getAssetFileUrl(assetId, false, true, publicSharedKey)} type="video/mp4" />
 		<track kind="captions" />
 	</video>
 

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

@@ -1,7 +1,6 @@
 <script lang="ts">
-	import { UserResponseDto } from '@api';
+	import { UserResponseDto, api } from '@api';
 	import { createEventDispatcher } from 'svelte';
-	import { page } from '$app/stores';
 	import { fade } from 'svelte/transition';
 	import Cog from 'svelte-material-icons/Cog.svelte';
 	import Logout from 'svelte-material-icons/Logout.svelte';
@@ -35,7 +34,7 @@
 					<img
 						transition:fade={{ duration: 100 }}
 						class:hidden={showProfilePictureFallback}
-						src={`${$page.url.origin}/api/user/profile-image/${user.id}`}
+						src={api.getProfileImageUrl(user.id)}
 						alt="profile-img"
 						class="inline rounded-full h-20 w-20 object-cover shadow-md border-2 border-immich-primary dark:border-immich-dark-primary"
 						draggable="false"

+ 1 - 1
web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte

@@ -126,7 +126,7 @@
 						{#if user.profileImagePath}
 							<img
 								class:hidden={showProfilePictureFallback}
-								src={`${$page.url.origin}/api/user/profile-image/${user.id}`}
+								src={api.getProfileImageUrl(user.id)}
 								alt="profile-img"
 								class="inline rounded-full h-12 w-12 object-cover shadow-md border-2 border-immich-primary hover:border-immich-dark-primary dark:hover:border-immich-primary dark:border-immich-dark-primary transition-all"
 								draggable="false"

+ 8 - 2
web/src/lib/components/sharedlinks-page/shared-link-card.svelte

@@ -1,5 +1,11 @@
 <script lang="ts">
-	import { api, AssetResponseDto, SharedLinkResponseDto, SharedLinkType } from '@api';
+	import {
+		api,
+		AssetResponseDto,
+		SharedLinkResponseDto,
+		SharedLinkType,
+		ThumbnailFormat
+	} from '@api';
 	import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 	import OpenInNew from 'svelte-material-icons/OpenInNew.svelte';
 	import Delete from 'svelte-material-icons/TrashCanOutline.svelte';
@@ -69,7 +75,7 @@
 		{:then asset}
 			<img
 				id={asset.id}
-				src={`/api/asset/thumbnail/${asset.id}?format=WEBP`}
+				src={api.getAssetThumbnailUrl(asset.id, ThumbnailFormat.Webp)}
 				alt={asset.id}
 				class="object-cover w-[100px] h-[100px] rounded-lg"
 				loading="lazy"

+ 1 - 9
web/src/lib/utils/asset-utils.ts

@@ -1,18 +1,10 @@
-import { api, AddAssetsResponseDto, AssetResponseDto, ThumbnailFormat } from '@api';
+import { api, AddAssetsResponseDto, AssetResponseDto } from '@api';
 import {
 	notificationController,
 	NotificationType
 } from '$lib/components/shared-components/notification/notification';
 import { downloadAssets } from '$lib/stores/download';
 
-export const getThumbnailUrl = (assetId: string, format: ThumbnailFormat, key?: string) => {
-	let url = `/api/asset/thumbnail/${assetId}?format=${format}`;
-	if (key) {
-		url += `&key=${key}`;
-	}
-	return url;
-};
-
 export const addAssetsToAlbum = async (
 	albumId: string,
 	assetIds: Array<string>,

+ 2 - 3
web/src/routes/(user)/share/[key]/+page.server.ts

@@ -1,6 +1,5 @@
 import { error } from '@sveltejs/kit';
-import { getThumbnailUrl } from '$lib/utils/asset-utils';
-import { ThumbnailFormat } from '@api';
+import { ThumbnailFormat, api as clientApi } from '@api';
 import type { PageServerLoad } from './$types';
 import featurePanelUrl from '$lib/assets/feature-panel.png';
 
@@ -19,7 +18,7 @@ export const load = (async ({ params, locals: { api } }) => {
 				title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
 				description: sharedLink.description || `${assetCount} shared photos & videos.`,
 				imageUrl: assetId
-					? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
+					? clientApi.getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
 					: featurePanelUrl
 			}
 		};