mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
fix(web): address review feedback on hero view transitions
Change-Id: I9f12e1616ddcf124a9926d54868b5e166a6a6964
This commit is contained in:
-5344
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
-16
@@ -1,16 +0,0 @@
|
|||||||
import { HttpError } from '@oazapfts/runtime';
|
|
||||||
export interface ApiValidationError {
|
|
||||||
code: string;
|
|
||||||
path: (string | number)[];
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
export interface ApiExceptionResponse {
|
|
||||||
message: string;
|
|
||||||
error?: string;
|
|
||||||
statusCode: number;
|
|
||||||
errors?: ApiValidationError[];
|
|
||||||
}
|
|
||||||
export interface ApiHttpError extends HttpError {
|
|
||||||
data: ApiExceptionResponse;
|
|
||||||
}
|
|
||||||
export declare function isHttpError(error: unknown): error is ApiHttpError;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { HttpError } from '@oazapfts/runtime';
|
|
||||||
export function isHttpError(error) {
|
|
||||||
return error instanceof HttpError;
|
|
||||||
}
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
export * from './fetch-client.js';
|
|
||||||
export * from './fetch-errors.js';
|
|
||||||
export interface InitOptions {
|
|
||||||
baseUrl: string;
|
|
||||||
apiKey: string;
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
}
|
|
||||||
export declare const init: ({ baseUrl, apiKey, headers }: InitOptions) => void;
|
|
||||||
export declare const getBaseUrl: () => string;
|
|
||||||
export declare const setBaseUrl: (baseUrl: string) => void;
|
|
||||||
export declare const setApiKey: (apiKey: string) => void;
|
|
||||||
export declare const setHeader: (key: string, value: string) => void;
|
|
||||||
export declare const setHeaders: (headers: Record<string, string>) => void;
|
|
||||||
export declare const getAssetOriginalPath: (id: string) => string;
|
|
||||||
export declare const getAssetThumbnailPath: (id: string) => string;
|
|
||||||
export declare const getAssetPlaybackPath: (id: string) => string;
|
|
||||||
export declare const getUserProfileImagePath: (userId: string) => string;
|
|
||||||
export declare const getPeopleThumbnailPath: (personId: string) => string;
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { defaults } from './fetch-client.js';
|
|
||||||
export * from './fetch-client.js';
|
|
||||||
export * from './fetch-errors.js';
|
|
||||||
export const init = ({ baseUrl, apiKey, headers }) => {
|
|
||||||
setBaseUrl(baseUrl);
|
|
||||||
setApiKey(apiKey);
|
|
||||||
if (headers) {
|
|
||||||
setHeaders(headers);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const getBaseUrl = () => defaults.baseUrl;
|
|
||||||
export const setBaseUrl = (baseUrl) => {
|
|
||||||
defaults.baseUrl = baseUrl;
|
|
||||||
};
|
|
||||||
export const setApiKey = (apiKey) => {
|
|
||||||
defaults.headers = defaults.headers || {};
|
|
||||||
defaults.headers['x-api-key'] = apiKey;
|
|
||||||
};
|
|
||||||
export const setHeader = (key, value) => {
|
|
||||||
assertNoApiKey(key);
|
|
||||||
defaults.headers = defaults.headers || {};
|
|
||||||
defaults.headers[key] = value;
|
|
||||||
};
|
|
||||||
export const setHeaders = (headers) => {
|
|
||||||
defaults.headers = defaults.headers || {};
|
|
||||||
for (const [key, value] of Object.entries(headers)) {
|
|
||||||
assertNoApiKey(key);
|
|
||||||
defaults.headers[key] = value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const assertNoApiKey = (headerKey) => {
|
|
||||||
if (headerKey.toLowerCase() === 'x-api-key') {
|
|
||||||
throw new Error('The API key header can only be set using setApiKey().');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const getAssetOriginalPath = (id) => `/assets/${id}/original`;
|
|
||||||
export const getAssetThumbnailPath = (id) => `/assets/${id}/thumbnail`;
|
|
||||||
export const getAssetPlaybackPath = (id) => `/assets/${id}/video/playback`;
|
|
||||||
export const getUserProfileImagePath = (userId) => `/users/${userId}/profile-image`;
|
|
||||||
export const getPeopleThumbnailPath = (personId) => `/people/${personId}/thumbnail`;
|
|
||||||
Generated
-15
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "immich-monorepo",
|
|
||||||
"version": "2.7.5",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "immich-monorepo",
|
|
||||||
"version": "2.7.5",
|
|
||||||
"engines": {
|
|
||||||
"pnpm": ">=10.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -10,6 +10,7 @@
|
|||||||
import { assetsSnapshot, filterIsInOrNearViewport } from '$lib/managers/timeline-manager/utils.svelte';
|
import { assetsSnapshot, filterIsInOrNearViewport } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import { viewTransitionManager } from '$lib/managers/ViewTransitionManager.svelte';
|
import { viewTransitionManager } from '$lib/managers/ViewTransitionManager.svelte';
|
||||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||||
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import type { CommonPosition } from '$lib/utils/layout-utils';
|
import type { CommonPosition } from '$lib/utils/layout-utils';
|
||||||
import { fromTimelinePlainDate, getDateLocaleString } from '$lib/utils/timeline-util';
|
import { fromTimelinePlainDate, getDateLocaleString } from '$lib/utils/timeline-util';
|
||||||
import { Icon } from '@immich/ui';
|
import { Icon } from '@immich/ui';
|
||||||
@@ -68,19 +69,21 @@
|
|||||||
if (!asset) {
|
if (!asset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void viewTransitionManager.startTransition({
|
handlePromiseError(
|
||||||
types: ['timeline'],
|
viewTransitionManager.startTransition({
|
||||||
performUpdate: async () => {
|
types: ['timeline'],
|
||||||
assetViewerManager.emit('ViewerCloseTransitionReady');
|
performUpdate: async () => {
|
||||||
const event = await eventManager.untilNext('TimelineLoaded');
|
assetViewerManager.emit('ViewerCloseTransitionReady');
|
||||||
toTimelineHeroAssetId = event.id;
|
const event = await eventManager.untilNext('TimelineLoaded');
|
||||||
await tick();
|
toTimelineHeroAssetId = event.id;
|
||||||
},
|
await tick();
|
||||||
onFinished: () => {
|
},
|
||||||
toTimelineHeroAssetId = null;
|
onFinished: () => {
|
||||||
focusAsset(asset.id);
|
toTimelineHeroAssetId = null;
|
||||||
},
|
focusAsset(asset.id);
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
if (viewTransitionManager.isSupported()) {
|
if (viewTransitionManager.isSupported()) {
|
||||||
onMount(() => assetViewerManager.on({ ViewerCloseTransition: handleViewerCloseTransition }));
|
onMount(() => assetViewerManager.on({ ViewerCloseTransition: handleViewerCloseTransition }));
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
||||||
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
||||||
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
|
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
|
||||||
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
||||||
import { type AlbumResponseDto, type PersonResponseDto, type UserResponseDto } from '@immich/sdk';
|
import { type AlbumResponseDto, type PersonResponseDto, type UserResponseDto } from '@immich/sdk';
|
||||||
@@ -581,11 +582,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openViewer = () => void navigate({ targetRoute: 'current', assetId: asset.id });
|
const openViewer = () => void navigate({ targetRoute: 'current', assetId: asset.id });
|
||||||
startViewerTransition(
|
handlePromiseError(
|
||||||
asset.id,
|
startViewerTransition(
|
||||||
openViewer,
|
asset.id,
|
||||||
(id) => (toViewerHeroAssetId = id),
|
openViewer,
|
||||||
() => (toViewerHeroAssetId = null),
|
(id) => (toViewerHeroAssetId = id),
|
||||||
|
() => (toViewerHeroAssetId = null),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -99,7 +99,9 @@
|
|||||||
|
|
||||||
const handleClose = async (assetId: string) => {
|
const handleClose = async (assetId: string) => {
|
||||||
if (viewTransitionManager.isSupported()) {
|
if (viewTransitionManager.isSupported()) {
|
||||||
const transitionReady = assetViewerManager.untilNext('ViewerCloseTransitionReady');
|
const transitionReady = assetViewerManager.untilNext('ViewerCloseTransitionReady', {
|
||||||
|
signal: AbortSignal.timeout(200),
|
||||||
|
});
|
||||||
assetViewerManager.emit('ViewerCloseTransition', { id: assetId });
|
assetViewerManager.emit('ViewerCloseTransition', { id: assetId });
|
||||||
await transitionReady;
|
await transitionReady;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { tick } from 'svelte';
|
|||||||
import { viewTransitionManager } from '$lib/managers/ViewTransitionManager.svelte';
|
import { viewTransitionManager } from '$lib/managers/ViewTransitionManager.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
|
|
||||||
export function startViewerTransition(
|
export async function startViewerTransition(
|
||||||
heroAssetId: string,
|
heroAssetId: string,
|
||||||
openViewer: () => void,
|
openViewer: () => void,
|
||||||
activateHeroAsset: (assetId: string) => void,
|
activateHeroAsset: (assetId: string) => void,
|
||||||
deactivateHeroAsset: () => void,
|
deactivateHeroAsset: () => void,
|
||||||
) {
|
) {
|
||||||
void viewTransitionManager.startTransition({
|
await viewTransitionManager.startTransition({
|
||||||
types: ['viewer'],
|
types: ['viewer'],
|
||||||
prepareOldSnapshot: () => {
|
prepareOldSnapshot: () => {
|
||||||
activateHeroAsset(heroAssetId);
|
activateHeroAsset(heroAssetId);
|
||||||
|
|||||||
Reference in New Issue
Block a user