mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
refactor(web) open api client (#7103)
* refactor: person api * refactor: shared link and others
This commit is contained in:
parent
5fc1d43012
commit
d8631a00bb
81 changed files with 638 additions and 656 deletions
|
@ -1,135 +1,15 @@
|
|||
import {
|
||||
AssetApi,
|
||||
AssetApiFp,
|
||||
AssetJobName,
|
||||
DownloadApi,
|
||||
JobName,
|
||||
PersonApi,
|
||||
SearchApi,
|
||||
SharedLinkApi,
|
||||
UserApiFp,
|
||||
base,
|
||||
common,
|
||||
configuration,
|
||||
} from '@immich/sdk/axios';
|
||||
import type { ApiParams as ApiParameters } from './types';
|
||||
import { AssetApi, DownloadApi, configuration } from '@immich/sdk/axios';
|
||||
|
||||
class ImmichApi {
|
||||
public downloadApi: DownloadApi;
|
||||
public assetApi: AssetApi;
|
||||
public searchApi: SearchApi;
|
||||
public sharedLinkApi: SharedLinkApi;
|
||||
public personApi: PersonApi;
|
||||
|
||||
private config: configuration.Configuration;
|
||||
private key?: string;
|
||||
|
||||
get isSharedLink() {
|
||||
return !!this.key;
|
||||
}
|
||||
|
||||
constructor(parameters: configuration.ConfigurationParameters) {
|
||||
this.config = new configuration.Configuration(parameters);
|
||||
|
||||
this.downloadApi = new DownloadApi(this.config);
|
||||
this.assetApi = new AssetApi(this.config);
|
||||
this.searchApi = new SearchApi(this.config);
|
||||
this.sharedLinkApi = new SharedLinkApi(this.config);
|
||||
this.personApi = new PersonApi(this.config);
|
||||
}
|
||||
|
||||
private createUrl(path: string, parameters?: Record<string, unknown>) {
|
||||
const searchParameters = new URLSearchParams();
|
||||
for (const key in parameters) {
|
||||
const value = parameters[key];
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParameters.set(key, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(path, common.DUMMY_BASE_URL);
|
||||
url.search = searchParameters.toString();
|
||||
|
||||
return (this.config.basePath || base.BASE_PATH) + common.toPathString(url);
|
||||
}
|
||||
|
||||
public setKey(key: string) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public getKey(): string | undefined {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public setAccessToken(accessToken: string) {
|
||||
this.config.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public removeAccessToken() {
|
||||
this.config.accessToken = undefined;
|
||||
}
|
||||
|
||||
public setBaseUrl(baseUrl: string) {
|
||||
this.config.basePath = baseUrl;
|
||||
}
|
||||
|
||||
public getAssetFileUrl(...[assetId, isThumb, isWeb]: ApiParameters<typeof AssetApiFp, 'serveFile'>) {
|
||||
const path = `/asset/file/${assetId}`;
|
||||
return this.createUrl(path, { isThumb, isWeb, key: this.getKey() });
|
||||
}
|
||||
|
||||
public getAssetThumbnailUrl(...[assetId, format]: ApiParameters<typeof AssetApiFp, 'getAssetThumbnail'>) {
|
||||
const path = `/asset/thumbnail/${assetId}`;
|
||||
return this.createUrl(path, { format, key: this.getKey() });
|
||||
}
|
||||
|
||||
public getProfileImageUrl(...[userId]: ApiParameters<typeof UserApiFp, 'getProfileImage'>) {
|
||||
const path = `/user/profile-image/${userId}`;
|
||||
return this.createUrl(path);
|
||||
}
|
||||
|
||||
public getPeopleThumbnailUrl(personId: string) {
|
||||
const path = `/person/${personId}/thumbnail`;
|
||||
return this.createUrl(path);
|
||||
}
|
||||
|
||||
public getJobName(jobName: JobName) {
|
||||
const names: Record<JobName, string> = {
|
||||
[JobName.ThumbnailGeneration]: 'Generate Thumbnails',
|
||||
[JobName.MetadataExtraction]: 'Extract Metadata',
|
||||
[JobName.Sidecar]: 'Sidecar Metadata',
|
||||
[JobName.SmartSearch]: 'Smart Search',
|
||||
[JobName.FaceDetection]: 'Face Detection',
|
||||
[JobName.FacialRecognition]: 'Facial Recognition',
|
||||
[JobName.VideoConversion]: 'Transcode Videos',
|
||||
[JobName.StorageTemplateMigration]: 'Storage Template Migration',
|
||||
[JobName.Migration]: 'Migration',
|
||||
[JobName.BackgroundTask]: 'Background Tasks',
|
||||
[JobName.Search]: 'Search',
|
||||
[JobName.Library]: 'Library',
|
||||
};
|
||||
|
||||
return names[jobName];
|
||||
}
|
||||
|
||||
public getAssetJobName(job: AssetJobName) {
|
||||
const names: Record<AssetJobName, string> = {
|
||||
[AssetJobName.RefreshMetadata]: 'Refresh metadata',
|
||||
[AssetJobName.RegenerateThumbnail]: 'Refresh thumbnails',
|
||||
[AssetJobName.TranscodeVideo]: 'Refresh encoded videos',
|
||||
};
|
||||
|
||||
return names[job];
|
||||
}
|
||||
|
||||
public getAssetJobMessage(job: AssetJobName) {
|
||||
const messages: Record<AssetJobName, string> = {
|
||||
[AssetJobName.RefreshMetadata]: 'Refreshing metadata',
|
||||
[AssetJobName.RegenerateThumbnail]: `Regenerating thumbnails`,
|
||||
[AssetJobName.TranscodeVideo]: `Refreshing encoded video`,
|
||||
};
|
||||
|
||||
return messages[job];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { finishOAuth, linkOAuthAccount, startOAuth, unlinkOAuthAccount } from '@immich/sdk';
|
||||
import type { UserResponseDto } from '@immich/sdk/axios';
|
||||
import { type UserResponseDto } from '@immich/sdk/axios';
|
||||
import type { AxiosError } from 'axios';
|
||||
import {
|
||||
NotificationType,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@api';
|
||||
import Badge from '$lib/components/elements/badge.svelte';
|
||||
import JobTileButton from './job-tile-button.svelte';
|
||||
import JobTileStatus from './job-tile-status.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
|
||||
import { JobCommand } from '@immich/sdk/axios';
|
||||
import {
|
||||
mdiAlertCircle,
|
||||
mdiAllInclusive,
|
||||
|
@ -16,6 +14,9 @@
|
|||
mdiPlay,
|
||||
mdiSelectionSearch,
|
||||
} from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import JobTileButton from './job-tile-button.svelte';
|
||||
import JobTileStatus from './job-tile-status.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let subtitle: string | undefined;
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { getJobName } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { type AllJobStatusResponseDto, api, JobCommand, type JobCommandDto, JobName } from '@api';
|
||||
import type { ComponentType } from 'svelte';
|
||||
import { sendJobCommand, type AllJobStatusResponseDto, type JobCommandDto } from '@immich/sdk';
|
||||
import { JobCommand, JobName } from '@immich/sdk/axios';
|
||||
import {
|
||||
mdiFaceRecognition,
|
||||
mdiFileJpgBox,
|
||||
|
@ -18,10 +19,10 @@
|
|||
mdiTagFaces,
|
||||
mdiVideo,
|
||||
} from '@mdi/js';
|
||||
import type { ComponentType } from 'svelte';
|
||||
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
|
||||
import JobTile from './job-tile.svelte';
|
||||
import StorageMigrationDescription from './storage-migration-description.svelte';
|
||||
import { sendJobCommand } from '@immich/sdk';
|
||||
|
||||
export let jobs: AllJobStatusResponseDto;
|
||||
|
||||
|
@ -59,23 +60,23 @@
|
|||
$: jobDetails = <Partial<Record<JobName, JobDetails>>>{
|
||||
[JobName.ThumbnailGeneration]: {
|
||||
icon: mdiFileJpgBox,
|
||||
title: api.getJobName(JobName.ThumbnailGeneration),
|
||||
title: getJobName(JobName.ThumbnailGeneration),
|
||||
subtitle: 'Generate large, small and blurred thumbnails for each asset, as well as thumbnails for each person',
|
||||
},
|
||||
[JobName.MetadataExtraction]: {
|
||||
icon: mdiTable,
|
||||
title: api.getJobName(JobName.MetadataExtraction),
|
||||
title: getJobName(JobName.MetadataExtraction),
|
||||
subtitle: 'Extract metadata information from each asset, such as GPS and resolution',
|
||||
},
|
||||
[JobName.Library]: {
|
||||
icon: mdiLibraryShelves,
|
||||
title: api.getJobName(JobName.Library),
|
||||
title: getJobName(JobName.Library),
|
||||
subtitle: 'Perform library tasks',
|
||||
allText: 'ALL',
|
||||
missingText: 'REFRESH',
|
||||
},
|
||||
[JobName.Sidecar]: {
|
||||
title: api.getJobName(JobName.Sidecar),
|
||||
title: getJobName(JobName.Sidecar),
|
||||
icon: mdiFileXmlBox,
|
||||
subtitle: 'Discover or synchronize sidecar metadata from the filesystem',
|
||||
allText: 'SYNC',
|
||||
|
@ -84,13 +85,13 @@
|
|||
},
|
||||
[JobName.SmartSearch]: {
|
||||
icon: mdiImageSearch,
|
||||
title: api.getJobName(JobName.SmartSearch),
|
||||
title: getJobName(JobName.SmartSearch),
|
||||
subtitle: 'Run machine learning on assets to support smart search',
|
||||
disabled: !$featureFlags.smartSearch,
|
||||
},
|
||||
[JobName.FaceDetection]: {
|
||||
icon: mdiFaceRecognition,
|
||||
title: api.getJobName(JobName.FaceDetection),
|
||||
title: getJobName(JobName.FaceDetection),
|
||||
subtitle:
|
||||
'Detect the faces in assets using machine learning. For videos, only the thumbnail is considered. "All" (re-)processes all assets. "Missing" queues assets that haven\'t been processed yet. Detected faces will be queued for Facial Recognition after Face Detection is complete, grouping them into existing or new people.',
|
||||
handleCommand: handleConfirmCommand,
|
||||
|
@ -98,7 +99,7 @@
|
|||
},
|
||||
[JobName.FacialRecognition]: {
|
||||
icon: mdiTagFaces,
|
||||
title: api.getJobName(JobName.FacialRecognition),
|
||||
title: getJobName(JobName.FacialRecognition),
|
||||
subtitle:
|
||||
'Group detected faces into people. This step runs after Face Detection is complete. "All" (re-)clusters all faces. "Missing" queues faces that don\'t have a person assigned.',
|
||||
handleCommand: handleConfirmCommand,
|
||||
|
@ -106,18 +107,18 @@
|
|||
},
|
||||
[JobName.VideoConversion]: {
|
||||
icon: mdiVideo,
|
||||
title: api.getJobName(JobName.VideoConversion),
|
||||
title: getJobName(JobName.VideoConversion),
|
||||
subtitle: 'Transcode videos for wider compatibility with browsers and devices',
|
||||
},
|
||||
[JobName.StorageTemplateMigration]: {
|
||||
icon: mdiFolderMove,
|
||||
title: api.getJobName(JobName.StorageTemplateMigration),
|
||||
title: getJobName(JobName.StorageTemplateMigration),
|
||||
allowForceCommand: false,
|
||||
component: StorageMigrationDescription,
|
||||
},
|
||||
[JobName.Migration]: {
|
||||
icon: mdiFolderMove,
|
||||
title: api.getJobName(JobName.Migration),
|
||||
title: getJobName(JobName.Migration),
|
||||
subtitle: 'Migrate thumbnails for assets and faces to the latest folder structure',
|
||||
allowForceCommand: false,
|
||||
},
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import type { ServerStatsResponseDto } from '@api';
|
||||
import { asByteUnitString, getBytesWithUnit } from '$lib/utils/byte-units';
|
||||
import StatsCard from './stats-card.svelte';
|
||||
import { mdiCameraIris, mdiChartPie, mdiPlayCircle } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { asByteUnitString, getBytesWithUnit } from '$lib/utils/byte-units';
|
||||
import type { ServerStatsResponseDto } from '@immich/sdk';
|
||||
import { mdiCameraIris, mdiChartPie, mdiPlayCircle } from '@mdi/js';
|
||||
import StatsCard from './stats-card.svelte';
|
||||
|
||||
export let stats: ServerStatsResponseDto = {
|
||||
photos: 0,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { ResetOptions } from '$lib/utils/dipatch';
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
|
||||
export type SettingsEventType = {
|
||||
reset: ResetOptions & { configKeys: Array<keyof SystemConfigDto> };
|
||||
|
|
|
@ -1,25 +1,18 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
AudioCodec,
|
||||
CQMode,
|
||||
type SystemConfigDto,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
TranscodePolicy,
|
||||
VideoCodec,
|
||||
} from '@api';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { type SystemConfigDto } from '@immich/sdk';
|
||||
import { AudioCodec, CQMode, ToneMapping, TranscodeHWAccel, TranscodePolicy, VideoCodec } from '@immich/sdk/axios';
|
||||
import { mdiHelpCircleOutline } from '@mdi/js';
|
||||
import { isEqual, sortBy } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsEventType } from '../admin-settings';
|
||||
import SettingAccordion from '../setting-accordion.svelte';
|
||||
import SettingButtonsRow from '../setting-buttons-row.svelte';
|
||||
import SettingCheckboxes from '../setting-checkboxes.svelte';
|
||||
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
|
||||
import SettingSelect from '../setting-select.svelte';
|
||||
import SettingSwitch from '../setting-switch.svelte';
|
||||
import SettingCheckboxes from '../setting-checkboxes.svelte';
|
||||
import { isEqual, sortBy } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import SettingAccordion from '../setting-accordion.svelte';
|
||||
import { mdiHelpCircleOutline } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import type { SettingsEventType } from '../admin-settings';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { api, JobName, type SystemConfigDto, type SystemConfigJobDto } from '@api';
|
||||
import { getJobName } from '$lib/utils';
|
||||
import { type SystemConfigDto, type SystemConfigJobDto } from '@immich/sdk';
|
||||
import { JobName } from '@immich/sdk/axios';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
@ -41,7 +43,7 @@
|
|||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
{disabled}
|
||||
label="{api.getJobName(jobName)} Concurrency"
|
||||
label="{getJobName(jobName)} Concurrency"
|
||||
desc=""
|
||||
bind:value={config.job[jobName].concurrency}
|
||||
required={true}
|
||||
|
@ -50,7 +52,7 @@
|
|||
{:else}
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label="{api.getJobName(jobName)} Concurrency"
|
||||
label="{getJobName(jobName)} Concurrency"
|
||||
desc=""
|
||||
value="1"
|
||||
disabled={true}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { LogLevel, type SystemConfigDto } from '@api';
|
||||
import { type SystemConfigDto } from '@immich/sdk';
|
||||
import { LogLevel } from '@immich/sdk/axios';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import SettingButtonsRow from '$lib/components/admin-page/settings/setting-buttons-row.svelte';
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { SettingsEventType } from '../admin-settings';
|
||||
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
|
||||
|
||||
export let savedConfig: SystemConfigDto;
|
||||
export let defaultConfig: SystemConfigDto;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigTemplateStorageOptionDto } from '@api';
|
||||
import type { SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
|
||||
import * as luxon from 'luxon';
|
||||
|
||||
export let options: SystemConfigTemplateStorageOptionDto;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
import SettingButtonsRow from '$lib/components/admin-page/settings/setting-buttons-row.svelte';
|
||||
import SettingSelect from '$lib/components/admin-page/settings/setting-select.svelte';
|
||||
import { Colorspace, type SystemConfigDto } from '@api';
|
||||
import { type SystemConfigDto } from '@immich/sdk';
|
||||
import { Colorspace } from '@immich/sdk/axios';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { SystemConfigDto } from '@api';
|
||||
import type { SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { ThumbnailFormat, api, type AlbumResponseDto } from '@api';
|
||||
import { getUserById } from '@immich/sdk';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
|
@ -19,7 +20,7 @@
|
|||
let showVerticalDots = false;
|
||||
|
||||
$: imageData = album.albumThumbnailAssetId
|
||||
? api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)
|
||||
? getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)
|
||||
: noThumbnailUrl;
|
||||
|
||||
const dispatchClick = createEventDispatcher<OnClick>();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { AlbumResponseDto } from '@api';
|
||||
import type { AlbumResponseDto } from '@immich/sdk';
|
||||
|
||||
export type OnShowContextMenu = {
|
||||
showalbumcontextmenu: OnShowContextMenuDetail;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { type AlbumResponseDto, type UserResponseDto } from '@api';
|
||||
import { type AlbumResponseDto, type UserResponseDto } from '@immich/sdk';
|
||||
import { getMyUserInfo, removeUserFromAlbum } from '@immich/sdk';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
|
|
|
@ -2,8 +2,13 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { api, type AlbumResponseDto, type SharedLinkResponseDto, type UserResponseDto } from '@api';
|
||||
import { getAllUsers } from '@immich/sdk';
|
||||
import {
|
||||
getAllSharedLinks,
|
||||
getAllUsers,
|
||||
type AlbumResponseDto,
|
||||
type SharedLinkResponseDto,
|
||||
type UserResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { mdiCheck, mdiLink, mdiShareCircle } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
|
@ -35,8 +40,7 @@
|
|||
});
|
||||
|
||||
const getSharedLinks = async () => {
|
||||
const { data } = await api.sharedLinkApi.getAllSharedLinks();
|
||||
|
||||
const data = await getAllSharedLinks();
|
||||
sharedLinks = data.filter((link) => link.album?.id === album.id);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetType } from '$lib/utils/asset-utils';
|
||||
import { autoGrowHeight } from '$lib/utils/autogrow';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isTenMinutesApart } from '$lib/utils/timesince';
|
||||
import {
|
||||
AssetTypeEnum,
|
||||
ReactionType,
|
||||
ThumbnailFormat,
|
||||
api,
|
||||
createActivity,
|
||||
deleteActivity,
|
||||
getActivities,
|
||||
type ActivityResponseDto,
|
||||
type AssetTypeEnum,
|
||||
type UserResponseDto,
|
||||
} from '@api';
|
||||
import { createActivity, deleteActivity, getActivities } from '@immich/sdk';
|
||||
} from '@immich/sdk';
|
||||
import { ReactionType, ThumbnailFormat } from '@immich/sdk/axios';
|
||||
import { mdiClose, mdiDotsVertical, mdiHeart, mdiSend } from '@mdi/js';
|
||||
import * as luxon from 'luxon';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
|
@ -191,7 +192,7 @@
|
|||
<div class="aspect-square w-[75px] h-[75px]">
|
||||
<img
|
||||
class="rounded-lg w-[75px] h-[75px] object-cover"
|
||||
src={api.getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
|
||||
src={getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
|
||||
alt="comment-thumbnail"
|
||||
/>
|
||||
</div>
|
||||
|
@ -237,7 +238,7 @@
|
|||
<div class="aspect-square w-[75px] h-[75px]">
|
||||
<img
|
||||
class="rounded-lg w-[75px] h-[75px] object-cover"
|
||||
src={api.getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
|
||||
src={getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
|
||||
alt="like-thumbnail"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { type AlbumResponseDto, ThumbnailFormat, api } from '@api';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { ThumbnailFormat, type AlbumResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
|
@ -32,7 +33,7 @@
|
|||
<div class="h-12 w-12 shrink-0 rounded-xl bg-slate-300">
|
||||
{#if album.albumThumbnailAssetId}
|
||||
<img
|
||||
src={api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
|
||||
src={getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
|
||||
alt={album.albumName}
|
||||
class="z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg"
|
||||
data-testid="album-image"
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script lang="ts">
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { getAssetJobName } from '$lib/utils';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
||||
import { AssetJobName, type AssetResponseDto, AssetTypeEnum, api } from '@api';
|
||||
import { AssetJobName, AssetTypeEnum, type AssetResponseDto } from '@api';
|
||||
import {
|
||||
mdiAlertOutline,
|
||||
mdiArrowLeft,
|
||||
|
@ -22,7 +24,6 @@
|
|||
import { createEventDispatcher } from 'svelte';
|
||||
import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
|
||||
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
export let showCopyButton: boolean;
|
||||
|
@ -182,16 +183,16 @@
|
|||
|
||||
<MenuOption
|
||||
on:click={() => onJobClick(AssetJobName.RefreshMetadata)}
|
||||
text={api.getAssetJobName(AssetJobName.RefreshMetadata)}
|
||||
text={getAssetJobName(AssetJobName.RefreshMetadata)}
|
||||
/>
|
||||
<MenuOption
|
||||
on:click={() => onJobClick(AssetJobName.RegenerateThumbnail)}
|
||||
text={api.getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
||||
text={getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
||||
/>
|
||||
{#if asset.type === AssetTypeEnum.Video}
|
||||
<MenuOption
|
||||
on:click={() => onJobClick(AssetJobName.TranscodeVideo)}
|
||||
text={api.getAssetJobName(AssetJobName.TranscodeVideo)}
|
||||
text={getAssetJobName(AssetJobName.TranscodeVideo)}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
import { stackAssetsStore } from '$lib/stores/stacked-asset.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getAssetJobMessage, isSharedLink } from '$lib/utils';
|
||||
import { addAssetsToAlbum, downloadFile } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
|
@ -19,7 +20,6 @@
|
|||
AssetJobName,
|
||||
AssetTypeEnum,
|
||||
ReactionType,
|
||||
api,
|
||||
type ActivityResponseDto,
|
||||
type AlbumResponseDto,
|
||||
type AssetResponseDto,
|
||||
|
@ -29,9 +29,13 @@
|
|||
createActivity,
|
||||
createAlbum,
|
||||
deleteActivity,
|
||||
deleteAssets,
|
||||
getActivities,
|
||||
getActivityStatistics,
|
||||
getAllAlbums,
|
||||
runAssetJobs,
|
||||
updateAsset,
|
||||
updateAssets,
|
||||
} from '@immich/sdk';
|
||||
import { mdiChevronLeft, mdiChevronRight, mdiImageBrokenVariant } from '@mdi/js';
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
||||
|
@ -235,7 +239,7 @@
|
|||
$: asset.id && !sharedLink && handleGetAllAlbums(); // Update the album information when the asset ID changes
|
||||
|
||||
const handleGetAllAlbums = async () => {
|
||||
if (api.isSharedLink) {
|
||||
if (isSharedLink()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -386,7 +390,7 @@
|
|||
|
||||
const trashAsset = async () => {
|
||||
try {
|
||||
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
|
||||
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
|
||||
|
||||
dispatch('action', { type: AssetAction.TRASH, asset });
|
||||
|
||||
|
@ -401,7 +405,7 @@
|
|||
|
||||
const deleteAsset = async () => {
|
||||
try {
|
||||
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
|
||||
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
|
||||
|
||||
dispatch('action', { type: AssetAction.DELETE, asset });
|
||||
|
||||
|
@ -418,7 +422,7 @@
|
|||
|
||||
const toggleFavorite = async () => {
|
||||
try {
|
||||
const { data } = await api.assetApi.updateAsset({
|
||||
const data = await updateAsset({
|
||||
id: asset.id,
|
||||
updateAssetDto: {
|
||||
isFavorite: !asset.isFavorite,
|
||||
|
@ -470,7 +474,7 @@
|
|||
|
||||
const toggleArchive = async () => {
|
||||
try {
|
||||
const { data } = await api.assetApi.updateAsset({
|
||||
const data = await updateAsset({
|
||||
id: asset.id,
|
||||
updateAssetDto: {
|
||||
isArchived: !asset.isArchived,
|
||||
|
@ -491,8 +495,8 @@
|
|||
|
||||
const handleRunJob = async (name: AssetJobName) => {
|
||||
try {
|
||||
await api.assetApi.runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
|
||||
notificationController.show({ type: NotificationType.Info, message: api.getAssetJobMessage(name) });
|
||||
await runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
|
||||
notificationController.show({ type: NotificationType.Info, message: getAssetJobMessage(name) });
|
||||
} catch (error) {
|
||||
await handleError(error, `Unable to submit job`);
|
||||
}
|
||||
|
@ -552,7 +556,7 @@
|
|||
const handleUnstack = async () => {
|
||||
try {
|
||||
const ids = $stackAssetsStore.map(({ id }) => id);
|
||||
await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids, removeParent: true } });
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, removeParent: true } });
|
||||
for (const child of $stackAssetsStore) {
|
||||
child.stackParentId = null;
|
||||
child.stackCount = 0;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
|
||||
import { getAssetFilename } from '$lib/utils/asset-utils';
|
||||
import { type AlbumResponseDto, type AssetResponseDto, ThumbnailFormat, api } from '@api';
|
||||
import { DateTime } from 'luxon';
|
||||
import { createEventDispatcher, onDestroy } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { asByteUnitString } from '../../utils/byte-units';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
|
||||
import { autoGrowHeight } from '$lib/utils/autogrow';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import { ThumbnailFormat, type AlbumResponseDto, type AssetResponseDto } from '@api';
|
||||
import { getAssetInfo, updateAsset } from '@immich/sdk';
|
||||
import {
|
||||
mdiCalendar,
|
||||
mdiCameraIris,
|
||||
|
@ -17,22 +20,21 @@
|
|||
mdiEye,
|
||||
mdiEyeOff,
|
||||
mdiImageOutline,
|
||||
mdiMapMarkerOutline,
|
||||
mdiInformationOutline,
|
||||
mdiMapMarkerOutline,
|
||||
mdiPencil,
|
||||
} from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import PersonSidePanel from '../faces-page/person-side-panel.svelte';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import Map from '../shared-components/map/map.svelte';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import ChangeLocation from '../shared-components/change-location.svelte';
|
||||
import { DateTime } from 'luxon';
|
||||
import { createEventDispatcher, onDestroy } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { asByteUnitString } from '../../utils/byte-units';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { autoGrowHeight } from '$lib/utils/autogrow';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import PersonSidePanel from '../faces-page/person-side-panel.svelte';
|
||||
import ChangeLocation from '../shared-components/change-location.svelte';
|
||||
import Map from '../shared-components/map/map.svelte';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
export let albums: AlbumResponseDto[] = [];
|
||||
|
@ -61,8 +63,8 @@
|
|||
description = newAsset?.exifInfo?.description || '';
|
||||
|
||||
// Get latest description from server
|
||||
if (newAsset.id && !api.isSharedLink) {
|
||||
const { data } = await api.assetApi.getAssetInfo({ id: asset.id });
|
||||
if (newAsset.id && !isSharedLink()) {
|
||||
const data = await getAssetInfo({ id: asset.id });
|
||||
people = data?.people || [];
|
||||
|
||||
description = data.exifInfo?.description || '';
|
||||
|
@ -127,9 +129,9 @@
|
|||
};
|
||||
|
||||
const handleRefreshPeople = async () => {
|
||||
await api.assetApi.getAssetInfo({ id: asset.id }).then((res) => {
|
||||
people = res.data?.people || [];
|
||||
textArea.value = res.data?.exifInfo?.description || '';
|
||||
await getAssetInfo({ id: asset.id }).then((data) => {
|
||||
people = data?.people || [];
|
||||
textArea.value = data?.exifInfo?.description || '';
|
||||
});
|
||||
showEditFaces = false;
|
||||
};
|
||||
|
@ -146,10 +148,7 @@
|
|||
originalDescription = description;
|
||||
dispatch('descriptionFocusOut');
|
||||
try {
|
||||
await api.assetApi.updateAsset({
|
||||
id: asset.id,
|
||||
updateAssetDto: { description },
|
||||
});
|
||||
await updateAsset({ id: asset.id, updateAssetDto: { description } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Cannot update the description');
|
||||
}
|
||||
|
@ -162,7 +161,7 @@
|
|||
async function handleConfirmChangeDate(dateTimeOriginal: string) {
|
||||
isShowChangeDate = false;
|
||||
try {
|
||||
await api.assetApi.updateAsset({ id: asset.id, updateAssetDto: { dateTimeOriginal } });
|
||||
await updateAsset({ id: asset.id, updateAssetDto: { dateTimeOriginal } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to change date');
|
||||
}
|
||||
|
@ -174,13 +173,7 @@
|
|||
isShowChangeLocation = false;
|
||||
|
||||
try {
|
||||
await api.assetApi.updateAsset({
|
||||
id: asset.id,
|
||||
updateAssetDto: {
|
||||
latitude: gps.lat,
|
||||
longitude: gps.lng,
|
||||
},
|
||||
});
|
||||
await updateAsset({ id: asset.id, updateAssetDto: { latitude: gps.lat, longitude: gps.lng } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to change location');
|
||||
}
|
||||
|
@ -219,7 +212,7 @@
|
|||
<section class="px-4 mt-10">
|
||||
{#key asset.id}
|
||||
<textarea
|
||||
disabled={!isOwner || api.isSharedLink}
|
||||
disabled={!isOwner || isSharedLink()}
|
||||
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"
|
||||
|
@ -238,7 +231,7 @@
|
|||
<p class="px-4 break-words whitespace-pre-line w-full text-black dark:text-white text-base">{description}</p>
|
||||
{/if}
|
||||
|
||||
{#if !api.isSharedLink && people.length > 0}
|
||||
{#if !isSharedLink() && people.length > 0}
|
||||
<section class="px-4 py-4 text-sm">
|
||||
<div class="flex h-10 w-full items-center justify-between">
|
||||
<h2>PEOPLE</h2>
|
||||
|
@ -284,7 +277,7 @@
|
|||
<ImageThumbnail
|
||||
curve
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
title={person.name}
|
||||
widthStyle="90px"
|
||||
|
@ -670,7 +663,7 @@
|
|||
alt={album.albumName}
|
||||
class="h-[50px] w-[50px] rounded object-cover"
|
||||
src={album.albumThumbnailAssetId &&
|
||||
api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
|
||||
getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
|
||||
draggable="false"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { api, type AssetResponseDto } from '@api';
|
||||
import { getKey } from '$lib/utils';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
|
||||
const loadAssetData = async () => {
|
||||
const { data } = await api.assetApi.serveFile(
|
||||
{ id: asset.id, isThumb: false, isWeb: false, key: api.getKey() },
|
||||
{ id: asset.id, isThumb: false, isWeb: false, key: getKey() },
|
||||
{ responseType: 'blob' },
|
||||
);
|
||||
if (data instanceof Blob) {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { api, type AssetResponseDto } from '@api';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import { useZoomImageWheel } from '@zoom-image/svelte';
|
||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
import { photoViewer } from '$lib/stores/assets.store';
|
||||
import { getBoundingBox } from '$lib/utils/people-utils';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
|
||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
|
||||
import { getBoundingBox } from '$lib/utils/people-utils';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
import { api, type AssetResponseDto } from '@api';
|
||||
import { useZoomImageWheel } from '@zoom-image/svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
export let element: HTMLDivElement | undefined = undefined;
|
||||
|
@ -50,7 +51,7 @@
|
|||
abortController = new AbortController();
|
||||
|
||||
const { data } = await api.assetApi.serveFile(
|
||||
{ id: asset.id, isThumb: false, isWeb: !loadOriginal, key: api.getKey() },
|
||||
{ id: asset.id, isThumb: false, isWeb: !loadOriginal, key: getKey() },
|
||||
{
|
||||
responseType: 'blob',
|
||||
signal: abortController.signal,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { ThumbnailFormat, api } from '@api';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { videoViewerVolume } from '$lib/stores/preferences.store';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { getAssetFileUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { ThumbnailFormat } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
|
||||
export let assetId: string;
|
||||
|
||||
|
@ -35,9 +36,9 @@
|
|||
on:canplay={handleCanPlay}
|
||||
on:ended={() => dispatch('onVideoEnded')}
|
||||
bind:volume={$videoViewerVolume}
|
||||
poster={api.getAssetThumbnailUrl(assetId, ThumbnailFormat.Jpeg)}
|
||||
poster={getAssetThumbnailUrl(assetId, ThumbnailFormat.Jpeg)}
|
||||
>
|
||||
<source src={api.getAssetFileUrl(assetId, false, true)} type="video/mp4" />
|
||||
<source src={getAssetFileUrl(assetId, false, true)} type="video/mp4" />
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { ProjectionType } from '$lib/constants';
|
||||
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { ProjectionType } from '$lib/constants';
|
||||
import { getAssetFileUrl, getAssetThumbnailUrl, isSharedLink } from '$lib/utils';
|
||||
import { timeToSeconds } from '$lib/utils/time-to-seconds';
|
||||
import { api, type AssetResponseDto, AssetTypeEnum, ThumbnailFormat } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImageThumbnail from './image-thumbnail.svelte';
|
||||
import VideoThumbnail from './video-thumbnail.svelte';
|
||||
import { AssetTypeEnum, ThumbnailFormat, type AssetResponseDto } from '@api';
|
||||
import {
|
||||
mdiArchiveArrowDownOutline,
|
||||
mdiCameraBurst,
|
||||
|
@ -17,7 +15,10 @@
|
|||
mdiMotionPlayOutline,
|
||||
mdiRotate360,
|
||||
} from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImageThumbnail from './image-thumbnail.svelte';
|
||||
import VideoThumbnail from './video-thumbnail.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
click: { asset: AssetResponseDto };
|
||||
|
@ -138,13 +139,13 @@
|
|||
/>
|
||||
|
||||
<!-- Favorite asset star -->
|
||||
{#if !api.isSharedLink && asset.isFavorite}
|
||||
{#if !isSharedLink() && asset.isFavorite}
|
||||
<div class="absolute bottom-2 left-2 z-10">
|
||||
<Icon path={mdiHeart} size="24" class="text-white" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !api.isSharedLink && showArchiveIcon && asset.isArchived}
|
||||
{#if !isSharedLink() && showArchiveIcon && asset.isArchived}
|
||||
<div class="absolute {asset.isFavorite ? 'bottom-10' : 'bottom-2'} left-2 z-10">
|
||||
<Icon path={mdiArchiveArrowDownOutline} size="24" class="text-white" />
|
||||
</div>
|
||||
|
@ -175,7 +176,7 @@
|
|||
|
||||
{#if asset.resized}
|
||||
<ImageThumbnail
|
||||
url={api.getAssetThumbnailUrl(asset.id, format)}
|
||||
url={getAssetThumbnailUrl(asset.id, format)}
|
||||
altText={asset.originalFileName}
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
|
@ -191,7 +192,7 @@
|
|||
{#if asset.type === AssetTypeEnum.Video}
|
||||
<div class="absolute top-0 h-full w-full">
|
||||
<VideoThumbnail
|
||||
url={api.getAssetFileUrl(asset.id, false, true)}
|
||||
url={getAssetFileUrl(asset.id, false, true)}
|
||||
enablePlayback={mouseOver}
|
||||
curve={selected}
|
||||
durationInSeconds={timeToSeconds(asset.duration)}
|
||||
|
@ -202,7 +203,7 @@
|
|||
{#if asset.type === AssetTypeEnum.Image && asset.livePhotoVideoId}
|
||||
<div class="absolute top-0 h-full w-full">
|
||||
<VideoThumbnail
|
||||
url={api.getAssetFileUrl(asset.livePhotoVideoId, false, true)}
|
||||
url={getAssetFileUrl(asset.livePhotoVideoId, false, true)}
|
||||
pauseIcon={mdiMotionPauseOutline}
|
||||
playIcon={mdiMotionPlayOutline}
|
||||
showTime={false}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { api, AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto, ThumbnailFormat } from '@api';
|
||||
import { maximumLengthSearchPeople, timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { photoViewer } from '$lib/stores/assets.store';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getPersonNameWithHiddenValue, searchNameLocal } from '$lib/utils/person';
|
||||
import { AssetTypeEnum, ThumbnailFormat, type AssetFaceResponseDto, type PersonResponseDto } from '@api';
|
||||
import { searchPerson } from '@immich/sdk';
|
||||
import { mdiArrowLeftThin, mdiClose, mdiMagnify, mdiPlus } from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { linear } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import Icon from '../elements/icon.svelte';
|
||||
import { mdiArrowLeftThin, mdiClose, mdiMagnify, mdiPlus } from '@mdi/js';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import { getPersonNameWithHiddenValue, searchNameLocal } from '$lib/utils/person';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { photoViewer } from '$lib/stores/assets.store';
|
||||
import { maximumLengthSearchPeople, timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import Icon from '../elements/icon.svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
|
||||
export let peopleWithFaces: AssetFaceResponseDto[];
|
||||
export let allPeople: PersonResponseDto[];
|
||||
|
@ -42,7 +44,7 @@
|
|||
if (assetType === AssetTypeEnum.Image) {
|
||||
image = $photoViewer;
|
||||
} else if (assetType === AssetTypeEnum.Video) {
|
||||
const data = await api.getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
|
||||
const data = await getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
|
||||
const img: HTMLImageElement = new Image();
|
||||
img.src = data;
|
||||
|
||||
|
@ -116,7 +118,7 @@
|
|||
}
|
||||
const timeout = setTimeout(() => (isShowLoadingSearch = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { data } = await api.searchApi.searchPerson({ name: searchName });
|
||||
const data = await searchPerson({ name: searchName });
|
||||
searchedPeople = data;
|
||||
searchedPeopleCopy = data;
|
||||
searchWord = searchName;
|
||||
|
@ -229,7 +231,7 @@
|
|||
<ImageThumbnail
|
||||
curve
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
title={getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
widthStyle="90px"
|
||||
|
@ -255,7 +257,7 @@
|
|||
<ImageThumbnail
|
||||
curve
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
title={getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
widthStyle="90px"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { api, type PersonResponseDto } from '@api';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { type PersonResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
|
||||
export let person: PersonResponseDto;
|
||||
export let selectable = false;
|
||||
|
@ -34,13 +35,7 @@
|
|||
class:dark:border-immich-dark-primary={border}
|
||||
class:border-immich-primary={border}
|
||||
>
|
||||
<ImageThumbnail
|
||||
{circle}
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="100%"
|
||||
shadow
|
||||
/>
|
||||
<ImageThumbnail {circle} url={getPeopleThumbnailUrl(person.id)} altText={person.name} widthStyle="100%" shadow />
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { ActionQueryParameterValue, AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { type PersonResponseDto } from '@api';
|
||||
import { getAllPeople, getPerson, mergePerson } from '@immich/sdk';
|
||||
import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { api, type PersonResponseDto } from '@api';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { goto } from '$app/navigation';
|
||||
import { ActionQueryParameterValue, AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import PeopleList from './people-list.svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let person: PersonResponseDto;
|
||||
let people: PersonResponseDto[] = [];
|
||||
|
@ -35,7 +36,7 @@
|
|||
);
|
||||
|
||||
onMount(async () => {
|
||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||
const data = await getAllPeople({ withHidden: false });
|
||||
people = data.people;
|
||||
});
|
||||
|
||||
|
@ -68,11 +69,11 @@
|
|||
|
||||
const handleMerge = async () => {
|
||||
try {
|
||||
let { data: results } = await api.personApi.mergePerson({
|
||||
let results = await mergePerson({
|
||||
id: person.id,
|
||||
mergePersonDto: { ids: selectedPeople.map(({ id }) => id) },
|
||||
});
|
||||
const { data: mergedPerson } = await api.personApi.getPerson({ id: person.id });
|
||||
const mergedPerson = await getPerson({ id: person.id });
|
||||
const count = results.filter(({ success }) => success).length;
|
||||
notificationController.show({
|
||||
message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`,
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import { api, type PersonResponseDto } from '@api';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { type PersonResponseDto } from '@api';
|
||||
import { mdiArrowLeft, mdiClose, mdiMerge } from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import { mdiArrowLeft, mdiClose, mdiMerge } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
|
||||
export let personMerge1: PersonResponseDto;
|
||||
export let personMerge2: PersonResponseDto;
|
||||
|
@ -60,7 +61,7 @@
|
|||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(personMerge1.id)}
|
||||
url={getPeopleThumbnailUrl(personMerge1.id)}
|
||||
altText={personMerge1.name}
|
||||
widthStyle="100%"
|
||||
/>
|
||||
|
@ -85,7 +86,7 @@
|
|||
border={potentialMergePeople.length > 0}
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(personMerge2.id)}
|
||||
url={getPeopleThumbnailUrl(personMerge2.id)}
|
||||
altText={personMerge2.name}
|
||||
widthStyle="100%"
|
||||
/>
|
||||
|
@ -104,7 +105,7 @@
|
|||
border={true}
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="100%"
|
||||
on:click={() => changePersonToMerge(person)}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { type PersonResponseDto, api } from '@api';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
||||
import { type PersonResponseDto } from '@api';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import IconButton from '../elements/buttons/icon-button.svelte';
|
||||
import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
|
||||
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
|
||||
import Portal from '../shared-components/portal/portal.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
|
||||
export let person: PersonResponseDto;
|
||||
export let preload = false;
|
||||
|
@ -50,7 +51,7 @@
|
|||
<ImageThumbnail
|
||||
shadow
|
||||
{preload}
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
title={person.name}
|
||||
widthStyle="100%"
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { api, type PersonResponseDto } from '@api';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { maximumLengthSearchPeople, timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { searchNameLocal } from '$lib/utils/person';
|
||||
import { type PersonResponseDto } from '@api';
|
||||
import { searchPerson } from '@immich/sdk';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import SearchBar from './search-bar.svelte';
|
||||
import { maximumLengthSearchPeople, timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
|
||||
export let screenHeight: number;
|
||||
export let people: PersonResponseDto[];
|
||||
|
@ -40,8 +41,7 @@
|
|||
|
||||
const timeout = setTimeout(() => (isSearchingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { data } = await api.searchApi.searchPerson({ name });
|
||||
people = data;
|
||||
people = await searchPerson({ name });
|
||||
searchWord = name;
|
||||
} catch (error) {
|
||||
handleError(error, "Can't search people");
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { linear } from 'svelte/easing';
|
||||
import { api, type PersonResponseDto, type AssetFaceResponseDto, AssetTypeEnum } from '@api';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import { mdiArrowLeftThin, mdiRestart } from '@mdi/js';
|
||||
import Icon from '../elements/icon.svelte';
|
||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import AssignFaceSidePanel from './assign-face-side-panel.svelte';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { getFaces, reassignFacesById } from '@immich/sdk';
|
||||
import { AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto } from '@api';
|
||||
import { createPerson, getAllPeople, getFaces, reassignFacesById } from '@immich/sdk';
|
||||
import { mdiArrowLeftThin, mdiRestart } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { linear } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import Icon from '../elements/icon.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import AssignFaceSidePanel from './assign-face-side-panel.svelte';
|
||||
|
||||
export let assetId: string;
|
||||
export let assetType: AssetTypeEnum;
|
||||
|
@ -69,8 +70,8 @@
|
|||
onMount(async () => {
|
||||
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { data } = await api.personApi.getAllPeople({ withHidden: true });
|
||||
allPeople = data.people;
|
||||
const { people } = await getAllPeople({ withHidden: true });
|
||||
allPeople = people;
|
||||
peopleWithFaces = await getFaces({ id: assetId });
|
||||
selectedPersonToCreate = Array.from({ length: peopleWithFaces.length });
|
||||
selectedPersonToReassign = Array.from({ length: peopleWithFaces.length });
|
||||
|
@ -115,7 +116,7 @@
|
|||
faceDto: { id: peopleWithFace.id },
|
||||
});
|
||||
} else if (selectedPersonToCreate[index]) {
|
||||
const { data } = await api.personApi.createPerson();
|
||||
const data = await createPerson();
|
||||
numberOfPersonToCreate.push(data.id);
|
||||
await reassignFacesById({
|
||||
id: data.id,
|
||||
|
@ -214,7 +215,7 @@
|
|||
curve
|
||||
shadow
|
||||
url={selectedPersonToCreate[index] ||
|
||||
api.getPeopleThumbnailUrl(selectedPersonToReassign[index]?.id || face.person.id)}
|
||||
getPeopleThumbnailUrl(selectedPersonToReassign[index]?.id || face.person.id)}
|
||||
altText={selectedPersonToReassign[index]
|
||||
? selectedPersonToReassign[index]?.name || ''
|
||||
: selectedPersonToCreate[index]
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { api, type AssetFaceUpdateItem, type PersonResponseDto } from '@api';
|
||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { mdiPlus, mdiMerge } from '@mdi/js';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import PeopleList from './people-list.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import {
|
||||
createPerson,
|
||||
getAllPeople,
|
||||
reassignFaces,
|
||||
type AssetFaceUpdateItem,
|
||||
type PersonResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { mdiMerge, mdiPlus } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import FaceThumbnail from './face-thumbnail.svelte';
|
||||
import PeopleList from './people-list.svelte';
|
||||
|
||||
export let assetIds: string[];
|
||||
export let personAssets: PersonResponseDto;
|
||||
|
@ -41,7 +47,7 @@
|
|||
}
|
||||
|
||||
onMount(async () => {
|
||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||
const data = await getAllPeople({ withHidden: false });
|
||||
people = data.people;
|
||||
});
|
||||
|
||||
|
@ -68,11 +74,8 @@
|
|||
|
||||
try {
|
||||
disableButtons = true;
|
||||
const { data } = await api.personApi.createPerson();
|
||||
await api.personApi.reassignFaces({
|
||||
id: data.id,
|
||||
assetFaceUpdateDto: { data: selectedPeople },
|
||||
});
|
||||
const data = await createPerson();
|
||||
await reassignFaces({ id: data.id, assetFaceUpdateDto: { data: selectedPeople } });
|
||||
|
||||
notificationController.show({
|
||||
message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to a new person`,
|
||||
|
@ -93,10 +96,7 @@
|
|||
try {
|
||||
disableButtons = true;
|
||||
if (selectedPerson) {
|
||||
await api.personApi.reassignFaces({
|
||||
id: selectedPerson.id,
|
||||
assetFaceUpdateDto: { data: selectedPeople },
|
||||
});
|
||||
await reassignFaces({ id: selectedPerson.id, assetFaceUpdateDto: { data: selectedPeople } });
|
||||
notificationController.show({
|
||||
message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to ${
|
||||
selectedPerson.name || 'an existing person'
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { memoryStore } from '$lib/stores/memory.store';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '@api';
|
||||
import { goto } from '$app/navigation';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import { fromLocalDateTime } from '$lib/utils/timeline-util';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { page } from '$app/stores';
|
||||
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
|
||||
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { tweened } from 'svelte/motion';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.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 { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { memoryStore } from '$lib/stores/memory.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { fromLocalDateTime } from '$lib/utils/timeline-util';
|
||||
import { getMemoryLane } from '@immich/sdk';
|
||||
import { mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiPause, mdiPlay } from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onMount } from 'svelte';
|
||||
import { tweened } from 'svelte/motion';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
const parseIndex = (s: string | null, max: number | null) =>
|
||||
Math.max(Math.min(Number.parseInt(s ?? '') || 0, max ?? 0), 0);
|
||||
|
@ -87,11 +88,10 @@
|
|||
onMount(async () => {
|
||||
if (!$memoryStore) {
|
||||
const localTime = new Date();
|
||||
const { data } = await api.assetApi.getMemoryLane({
|
||||
$memoryStore = await getMemoryLane({
|
||||
month: localTime.getMonth() + 1,
|
||||
day: localTime.getDate(),
|
||||
});
|
||||
$memoryStore = data;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -168,7 +168,7 @@
|
|||
<button class="relative h-full w-full rounded-2xl" disabled={!previousMemory} on:click={toPreviousMemory}>
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
src={previousMemory ? api.getAssetThumbnailUrl(previousMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
|
||||
src={previousMemory ? getAssetThumbnailUrl(previousMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
|
||||
alt=""
|
||||
draggable="false"
|
||||
/>
|
||||
|
@ -191,7 +191,7 @@
|
|||
<img
|
||||
transition:fade
|
||||
class="h-full w-full rounded-2xl object-contain transition-all"
|
||||
src={api.getAssetThumbnailUrl(currentAsset.id, 'JPEG')}
|
||||
src={getAssetThumbnailUrl(currentAsset.id, 'JPEG')}
|
||||
alt=""
|
||||
draggable="false"
|
||||
/>
|
||||
|
@ -231,7 +231,7 @@
|
|||
<button class="relative h-full w-full rounded-2xl" on:click={toNextMemory} disabled={!nextMemory}>
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
src={nextMemory ? api.getAssetThumbnailUrl(nextMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
|
||||
src={nextMemory ? getAssetThumbnailUrl(nextMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
|
||||
alt=""
|
||||
draggable="false"
|
||||
/>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import type { OnArchive } from '$lib/utils/actions';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api } from '@api';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { mdiArchiveArrowUpOutline, mdiArchiveArrowDownOutline, mdiTimerSand } from '@mdi/js';
|
||||
import type { OnArchive } from '$lib/utils/actions';
|
||||
|
||||
export let onArchive: OnArchive | undefined = undefined;
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
if (ids.length > 0) {
|
||||
await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids, isArchived } });
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, isArchived } });
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { getAssetJobMessage, getAssetJobName } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { AssetJobName, AssetTypeEnum, api } from '@api';
|
||||
import { AssetJobName, AssetTypeEnum } from '@api';
|
||||
import { runAssetJobs } from '@immich/sdk';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
|
||||
export let jobs: AssetJobName[] = [
|
||||
|
@ -21,8 +23,8 @@
|
|||
const handleRunJob = async (name: AssetJobName) => {
|
||||
try {
|
||||
const ids = [...getOwnedAssets()].map(({ id }) => id);
|
||||
await api.assetApi.runAssetJobs({ assetJobsDto: { assetIds: ids, name } });
|
||||
notificationController.show({ message: api.getAssetJobMessage(name), type: NotificationType.Info });
|
||||
await runAssetJobs({ assetJobsDto: { assetIds: ids, name } });
|
||||
notificationController.show({ message: getAssetJobMessage(name), type: NotificationType.Info });
|
||||
clearSelect();
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to submit job');
|
||||
|
@ -32,6 +34,6 @@
|
|||
|
||||
{#each jobs as job}
|
||||
{#if isAllVideos || job !== AssetJobName.TranscodeVideo}
|
||||
<MenuOption text={api.getAssetJobName(job)} on:click={() => handleRunJob(job)} />
|
||||
<MenuOption text={getAssetJobName(job)} on:click={() => handleRunJob(job)} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api } from '@api';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
||||
export let menuItem = false;
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
|
@ -17,9 +17,7 @@
|
|||
const ids = getSelectedAssets(getOwnedAssets(), $user);
|
||||
|
||||
try {
|
||||
await api.assetApi.updateAssets({
|
||||
assetBulkUpdateDto: { ids, dateTimeOriginal },
|
||||
});
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, dateTimeOriginal } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to change date');
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { api } from '@api';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import ChangeLocation from '$lib/components/shared-components/change-location.svelte';
|
||||
import { handleError } from '../../../utils/handle-error';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
|
||||
export let menuItem = false;
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
@ -17,13 +17,7 @@
|
|||
const ids = getSelectedAssets(getOwnedAssets(), $user);
|
||||
|
||||
try {
|
||||
await api.assetApi.updateAssets({
|
||||
assetBulkUpdateDto: {
|
||||
ids,
|
||||
latitude: point.lat,
|
||||
longitude: point.lng,
|
||||
},
|
||||
});
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to update location');
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api } from '@api';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
||||
import type { OnFavorite } from '$lib/utils/actions';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
|
||||
export let onFavorite: OnFavorite | undefined = undefined;
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
|||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
if (ids.length > 0) {
|
||||
await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids, isFavorite } });
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, isFavorite } });
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<script lang="ts">
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import { type SharedLinkResponseDto, api } from '@api';
|
||||
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { NotificationType, notificationController } from '../../shared-components/notification/notification';
|
||||
import { handleError } from '../../../utils/handle-error';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { type SharedLinkResponseDto } from '@api';
|
||||
import { removeSharedLinkAssets } from '@immich/sdk';
|
||||
import { mdiDeleteOutline } from '@mdi/js';
|
||||
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
|
||||
import { NotificationType, notificationController } from '../../shared-components/notification/notification';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
|
||||
export let sharedLink: SharedLinkResponseDto;
|
||||
|
||||
|
@ -15,12 +17,12 @@
|
|||
|
||||
const handleRemove = async () => {
|
||||
try {
|
||||
const { data: results } = await api.sharedLinkApi.removeSharedLinkAssets({
|
||||
const results = await removeSharedLinkAssets({
|
||||
id: sharedLink.id,
|
||||
assetIdsDto: {
|
||||
assetIds: [...getAssets()].map((asset) => asset.id),
|
||||
},
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
});
|
||||
|
||||
for (const result of results) {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { api } from '@api';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import type { OnStack } from '$lib/utils/actions';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
|
||||
export let onStack: OnStack | undefined;
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
|||
const ids = children.map(({ id }) => id);
|
||||
|
||||
if (children.length > 0) {
|
||||
await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids, stackParentId: parent.id } });
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, stackParentId: parent.id } });
|
||||
}
|
||||
|
||||
let childrenCount = parent.stackCount ?? 0;
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '@api';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { memoryStore } from '$lib/stores/memory.store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { memoryStore } from '$lib/stores/memory.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getMemoryLane } from '@immich/sdk';
|
||||
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
$: shouldRender = $memoryStore?.length > 0;
|
||||
|
||||
onMount(async () => {
|
||||
const localTime = new Date();
|
||||
const { data } = await api.assetApi.getMemoryLane({
|
||||
month: localTime.getMonth() + 1,
|
||||
day: localTime.getDate(),
|
||||
});
|
||||
$memoryStore = data;
|
||||
$memoryStore = await getMemoryLane({ month: localTime.getMonth() + 1, day: localTime.getDate() });
|
||||
});
|
||||
|
||||
let memoryLaneElement: HTMLElement;
|
||||
|
@ -76,7 +73,7 @@
|
|||
>
|
||||
<img
|
||||
class="h-full w-full rounded-xl object-cover"
|
||||
src={api.getAssetThumbnailUrl(memory.assets[0].id, 'JPEG')}
|
||||
src={getAssetThumbnailUrl(memory.assets[0].id, 'JPEG')}
|
||||
alt={memory.title}
|
||||
draggable="false"
|
||||
/>
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||
import { api, type AssetResponseDto, type SharedLinkResponseDto } from '@api';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { type AssetResponseDto, type SharedLinkResponseDto } from '@api';
|
||||
import { addSharedLinkAssets } from '@immich/sdk';
|
||||
import { mdiArrowLeft, mdiFileImagePlusOutline, mdiFolderDownloadOutline, mdiSelectAll } from '@mdi/js';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import DownloadAction from '../photos-page/actions/download-action.svelte';
|
||||
import RemoveFromSharedLink from '../photos-page/actions/remove-from-shared-link.svelte';
|
||||
|
@ -11,10 +16,7 @@
|
|||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import GalleryViewer from '../shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { mdiArrowLeft, mdiFileImagePlusOutline, mdiFolderDownloadOutline, mdiSelectAll } from '@mdi/js';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
|
||||
export let sharedLink: SharedLinkResponseDto;
|
||||
export let isOwned: boolean;
|
||||
|
@ -41,12 +43,12 @@
|
|||
results = await (!files || files.length === 0 || !Array.isArray(files)
|
||||
? openFileUploadDialog()
|
||||
: fileUploadHandler(files));
|
||||
const { data } = await api.sharedLinkApi.addSharedLinkAssets({
|
||||
const data = await addSharedLinkAssets({
|
||||
id: sharedLink.id,
|
||||
assetIdsDto: {
|
||||
assetIds: results.filter((id) => !!id) as string[],
|
||||
},
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
});
|
||||
|
||||
const added = data.filter((item) => item.success).length;
|
||||
|
|
|
@ -4,16 +4,17 @@
|
|||
} 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 { api, copyToClipboard, makeSharedLinkUrl, type SharedLinkResponseDto, SharedLinkType } from '@api';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { serverConfig } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { SharedLinkType, copyToClipboard, makeSharedLinkUrl, type SharedLinkResponseDto } from '@api';
|
||||
import { createSharedLink, updateSharedLink } from '@immich/sdk';
|
||||
import { mdiLink } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import BaseModal from '../base-modal.svelte';
|
||||
import type { ImmichDropDownOption } from '../dropdown-button.svelte';
|
||||
import DropdownButton from '../dropdown-button.svelte';
|
||||
import { notificationController, NotificationType } from '../notification/notification';
|
||||
import { mdiLink } from '@mdi/js';
|
||||
import { serverConfig } from '$lib/stores/server-config.store';
|
||||
import { NotificationType, notificationController } from '../notification/notification';
|
||||
|
||||
export let albumId: string | undefined = undefined;
|
||||
export let assetIds: string[] = [];
|
||||
|
@ -70,7 +71,7 @@
|
|||
const expirationDate = expirationTime ? new Date(currentTime + expirationTime).toISOString() : undefined;
|
||||
|
||||
try {
|
||||
const { data } = await api.sharedLinkApi.createSharedLink({
|
||||
const data = await createSharedLink({
|
||||
sharedLinkCreateDto: {
|
||||
type: shareType,
|
||||
albumId,
|
||||
|
@ -135,7 +136,7 @@
|
|||
? new Date(currentTime + expirationTime).toISOString()
|
||||
: null;
|
||||
|
||||
await api.sharedLinkApi.updateSharedLink({
|
||||
await updateSharedLink({
|
||||
id: editingLink.id,
|
||||
sharedLinkEditDto: {
|
||||
description,
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { Theme } from '$lib/constants';
|
||||
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
|
||||
import { api, type MapMarkerResponseDto } from '@api';
|
||||
import { getMapStyle } from '@immich/sdk';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getMapStyle, type MapMarkerResponseDto } from '@immich/sdk';
|
||||
import { mdiCog, mdiMapMarker } from '@mdi/js';
|
||||
import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson';
|
||||
import type { GeoJSONSource, LngLatLike, StyleSpecification } from 'maplibre-gl';
|
||||
|
@ -174,7 +174,7 @@
|
|||
/>
|
||||
{:else}
|
||||
<img
|
||||
src={api.getAssetThumbnailUrl(feature.properties?.id)}
|
||||
src={getAssetThumbnailUrl(feature.properties?.id, undefined)}
|
||||
class="rounded-full w-[60px] h-[60px] border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
|
||||
alt={`Image with id ${feature.properties?.id}`}
|
||||
/>
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
import { AppRoute } from '$lib/constants';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { UserAvatarColor } from '@api';
|
||||
import { deleteProfileImage, updateUser } from '@immich/sdk';
|
||||
import { deleteProfileImage, updateUser, type UserAvatarColor } from '@immich/sdk';
|
||||
import { mdiCog, mdiLogout, mdiPencil } from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { notificationController, NotificationType } from '../notification/notification';
|
||||
import { NotificationType, notificationController } from '../notification/notification';
|
||||
import UserAvatar from '../user-avatar.svelte';
|
||||
import AvatarSelector from './avatar-selector.svelte';
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<script lang="ts">
|
||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { SearchSuggestionType, type PersonResponseDto } from '@api';
|
||||
import { getAllPeople, getSearchSuggestions } from '@immich/sdk';
|
||||
import { mdiArrowRight, mdiClose } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
import Combobox, { type ComboBoxOption } from '../combobox.svelte';
|
||||
import { SearchSuggestionType, api, type PersonResponseDto } from '@api';
|
||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { mdiArrowRight, mdiClose } from '@mdi/js';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
enum MediaType {
|
||||
All = 'all',
|
||||
|
@ -108,8 +110,8 @@
|
|||
|
||||
const getPeople = async () => {
|
||||
try {
|
||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||
suggestions.people = data.people;
|
||||
const { people } = await getAllPeople({ withHidden: false });
|
||||
suggestions.people = people;
|
||||
} catch (error) {
|
||||
handleError(error, 'Failed to get people');
|
||||
}
|
||||
|
@ -143,8 +145,8 @@
|
|||
}
|
||||
|
||||
try {
|
||||
const { data } = await api.searchApi.getSearchSuggestions({
|
||||
type: type,
|
||||
const data = await getSearchSuggestions({
|
||||
$type: type,
|
||||
country: params.country,
|
||||
state: params.state,
|
||||
make: params.cameraMake,
|
||||
|
@ -251,7 +253,7 @@
|
|||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="100px"
|
||||
/>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import { page } from '$app/stores';
|
||||
import { locale, sidebarSettings } from '$lib/stores/preferences.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { api, type AssetApiGetAssetStatisticsRequest } from '@api';
|
||||
import { getAlbumCount } from '@immich/sdk';
|
||||
import { type AssetApiGetAssetStatisticsRequest } from '@api';
|
||||
import { getAlbumCount, getAssetStatistics } from '@immich/sdk';
|
||||
import {
|
||||
mdiAccount,
|
||||
mdiAccountMultiple,
|
||||
|
@ -24,10 +24,7 @@
|
|||
import SideBarButton from './side-bar-button.svelte';
|
||||
import SideBarSection from './side-bar-section.svelte';
|
||||
|
||||
const getStats = async (dto: AssetApiGetAssetStatisticsRequest) => {
|
||||
const { data: stats } = await api.assetApi.getAssetStatistics(dto);
|
||||
return stats;
|
||||
};
|
||||
const getStats = (dto: AssetApiGetAssetStatisticsRequest) => getAssetStatistics(dto);
|
||||
|
||||
const handleAlbumCount = async () => {
|
||||
try {
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getProfileImageUrl } from '$lib/utils';
|
||||
import { type UserAvatarColor } from '@immich/sdk';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { UserAvatarColor, api } from '@api';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
|
@ -74,7 +75,7 @@
|
|||
{#if showProfileImage && user.profileImagePath}
|
||||
<img
|
||||
bind:this={img}
|
||||
src={api.getProfileImageUrl(user.id)}
|
||||
src={getProfileImageUrl(user.id)}
|
||||
alt="Profile image of {title}"
|
||||
class="h-full w-full object-cover"
|
||||
class:hidden={showFallback}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { api, type AssetResponseDto, type SharedLinkResponseDto, SharedLinkType, ThumbnailFormat } from '@api';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import * as luxon from 'luxon';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { mdiCircleEditOutline, mdiContentCopy, mdiDelete, mdiOpenInNew } from '@mdi/js';
|
||||
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { SharedLinkType, ThumbnailFormat, type AssetResponseDto, type SharedLinkResponseDto } from '@api';
|
||||
import { getAssetInfo } from '@immich/sdk';
|
||||
import { mdiCircleEditOutline, mdiContentCopy, mdiDelete, mdiOpenInNew } from '@mdi/js';
|
||||
import * as luxon from 'luxon';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
|
||||
export let link: SharedLinkResponseDto;
|
||||
|
||||
|
@ -28,9 +30,7 @@
|
|||
assetId = link.assets[0].id;
|
||||
}
|
||||
|
||||
const { data } = await api.assetApi.getAssetInfo({ id: assetId });
|
||||
|
||||
return data;
|
||||
return getAssetInfo({ id: assetId });
|
||||
};
|
||||
|
||||
const getCountDownExpirationDate = () => {
|
||||
|
@ -72,7 +72,7 @@
|
|||
{:then asset}
|
||||
<img
|
||||
id={asset.id}
|
||||
src={api.getAssetThumbnailUrl(asset.id, ThumbnailFormat.Webp)}
|
||||
src={getAssetThumbnailUrl(asset.id, ThumbnailFormat.Webp)}
|
||||
alt={asset.id}
|
||||
class="h-[100px] w-[100px] rounded-lg object-cover"
|
||||
loading="lazy"
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { getKey } from '$lib/utils';
|
||||
import { type AssetResponseDto } from '@api';
|
||||
import { getAssetInfo } from '@immich/sdk';
|
||||
import { writable } from 'svelte/store';
|
||||
import { api, type AssetResponseDto } from '@api';
|
||||
|
||||
function createAssetViewingStore() {
|
||||
const viewingAssetStoreState = writable<AssetResponseDto>();
|
||||
const viewState = writable<boolean>(false);
|
||||
|
||||
const setAssetId = async (id: string) => {
|
||||
const { data } = await api.assetApi.getAssetInfo({ id, key: api.getKey() });
|
||||
const data = await getAssetInfo({ id, key: getKey() });
|
||||
viewingAssetStoreState.set(data);
|
||||
viewState.set(true);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { api, type AssetApiGetTimeBucketsRequest, type AssetResponseDto, TimeBucketSize } from '@api';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { getTimeBucket, getTimeBuckets, type AssetResponseDto } from '@immich/sdk';
|
||||
import { TimeBucketSize, type AssetApiGetTimeBucketsRequest } from '@immich/sdk/axios';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { DateTime } from 'luxon';
|
||||
import { type Unsubscriber, writable } from 'svelte/store';
|
||||
import { writable, type Unsubscriber } from 'svelte/store';
|
||||
import { handleError } from '../utils/handle-error';
|
||||
import { websocketStore } from './websocket';
|
||||
|
||||
|
@ -155,9 +157,9 @@ export class AssetStore {
|
|||
this.assetToBucket = {};
|
||||
this.albumAssets = new Set();
|
||||
|
||||
const { data: buckets } = await api.assetApi.getTimeBuckets({
|
||||
const buckets = await getTimeBuckets({
|
||||
...this.options,
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
|
@ -209,22 +211,22 @@ export class AssetStore {
|
|||
|
||||
bucket.cancelToken = new AbortController();
|
||||
|
||||
const { data: assets } = await api.assetApi.getTimeBucket(
|
||||
const assets = await getTimeBucket(
|
||||
{
|
||||
...this.options,
|
||||
timeBucket: bucketDate,
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
},
|
||||
{ signal: bucket.cancelToken.signal },
|
||||
);
|
||||
|
||||
if (this.albumId) {
|
||||
const { data: albumAssets } = await api.assetApi.getTimeBucket(
|
||||
const albumAssets = await getTimeBucket(
|
||||
{
|
||||
albumId: this.albumId,
|
||||
timeBucket: bucketDate,
|
||||
size: this.options.size,
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
},
|
||||
{ signal: bucket.cancelToken.signal },
|
||||
);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { defaults } from '@immich/sdk';
|
||||
import { AssetJobName, JobName, ThumbnailFormat, common } from '@immich/sdk/axios';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
interface UpdateParamAction {
|
||||
|
@ -31,3 +33,91 @@ export const updateParamList = async ({ param, value, add }: UpdateParamAction)
|
|||
|
||||
await goto(`?${searchParams.toString()}`, { replaceState: true, noScroll: true, keepFocus: true });
|
||||
};
|
||||
|
||||
export const getJobName = (jobName: JobName) => {
|
||||
const names: Record<JobName, string> = {
|
||||
[JobName.ThumbnailGeneration]: 'Generate Thumbnails',
|
||||
[JobName.MetadataExtraction]: 'Extract Metadata',
|
||||
[JobName.Sidecar]: 'Sidecar Metadata',
|
||||
[JobName.SmartSearch]: 'Smart Search',
|
||||
[JobName.FaceDetection]: 'Face Detection',
|
||||
[JobName.FacialRecognition]: 'Facial Recognition',
|
||||
[JobName.VideoConversion]: 'Transcode Videos',
|
||||
[JobName.StorageTemplateMigration]: 'Storage Template Migration',
|
||||
[JobName.Migration]: 'Migration',
|
||||
[JobName.BackgroundTask]: 'Background Tasks',
|
||||
[JobName.Search]: 'Search',
|
||||
[JobName.Library]: 'Library',
|
||||
};
|
||||
|
||||
return names[jobName];
|
||||
};
|
||||
|
||||
let _key: string | undefined;
|
||||
|
||||
export const setKey = (key: string) => {
|
||||
_key = key;
|
||||
};
|
||||
|
||||
export const getKey = (): string | undefined => {
|
||||
return _key;
|
||||
};
|
||||
|
||||
export const isSharedLink = () => {
|
||||
return !!_key;
|
||||
};
|
||||
|
||||
const createUrl = (path: string, parameters?: Record<string, unknown>) => {
|
||||
const searchParameters = new URLSearchParams();
|
||||
for (const key in parameters) {
|
||||
const value = parameters[key];
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParameters.set(key, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(path, 'https://example.com');
|
||||
url.search = searchParameters.toString();
|
||||
|
||||
return defaults.baseUrl + common.toPathString(url);
|
||||
};
|
||||
|
||||
export const getAssetFileUrl = (...[assetId, isWeb, isThumb]: [string, boolean, boolean]) => {
|
||||
const path = `/asset/file/${assetId}`;
|
||||
return createUrl(path, { isThumb, isWeb, key: getKey() });
|
||||
};
|
||||
|
||||
export const getAssetThumbnailUrl = (...[assetId, format]: [string, ThumbnailFormat | undefined]) => {
|
||||
const path = `/asset/thumbnail/${assetId}`;
|
||||
return createUrl(path, { format, key: getKey() });
|
||||
};
|
||||
|
||||
export const getProfileImageUrl = (...[userId]: [string]) => {
|
||||
const path = `/user/profile-image/${userId}`;
|
||||
return createUrl(path);
|
||||
};
|
||||
|
||||
export const getPeopleThumbnailUrl = (personId: string) => {
|
||||
const path = `/person/${personId}/thumbnail`;
|
||||
return createUrl(path);
|
||||
};
|
||||
|
||||
export const getAssetJobName = (job: AssetJobName) => {
|
||||
const names: Record<AssetJobName, string> = {
|
||||
[AssetJobName.RefreshMetadata]: 'Refresh metadata',
|
||||
[AssetJobName.RegenerateThumbnail]: 'Refresh thumbnails',
|
||||
[AssetJobName.TranscodeVideo]: 'Refresh encoded videos',
|
||||
};
|
||||
|
||||
return names[job];
|
||||
};
|
||||
|
||||
export const getAssetJobMessage = (job: AssetJobName) => {
|
||||
const messages: Record<AssetJobName, string> = {
|
||||
[AssetJobName.RefreshMetadata]: 'Refreshing metadata',
|
||||
[AssetJobName.RegenerateThumbnail]: `Regenerating thumbnails`,
|
||||
[AssetJobName.TranscodeVideo]: `Refreshing encoded video`,
|
||||
};
|
||||
|
||||
return messages[job];
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||
import { api } from '@api';
|
||||
import { deleteAssets as deleteBulk } from '@immich/sdk';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export type OnDelete = (assetId: string) => void;
|
||||
|
@ -10,7 +10,7 @@ export type OnStack = (ids: string[]) => void;
|
|||
|
||||
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
|
||||
try {
|
||||
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids, force } });
|
||||
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
|
||||
for (const id of ids) {
|
||||
onAssetDelete(id);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { downloadManager } from '$lib/stores/download';
|
|||
import { api } from '@api';
|
||||
import {
|
||||
addAssetsToAlbum as addAssets,
|
||||
getDownloadInfo,
|
||||
type AssetResponseDto,
|
||||
type AssetTypeEnum,
|
||||
type BulkIdResponseDto,
|
||||
|
@ -11,13 +12,14 @@ import {
|
|||
type UserResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { getKey } from '../utils';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export const addAssetsToAlbum = async (albumId: string, assetIds: Array<string>): Promise<BulkIdResponseDto[]> =>
|
||||
addAssets({
|
||||
id: albumId,
|
||||
bulkIdsDto: { ids: assetIds },
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
}).then((results) => {
|
||||
const count = results.filter(({ success }) => success).length;
|
||||
notificationController.show({
|
||||
|
@ -46,8 +48,7 @@ export const downloadArchive = async (fileName: string, options: DownloadInfoDto
|
|||
let downloadInfo: DownloadResponseDto | null = null;
|
||||
|
||||
try {
|
||||
const { data } = await api.downloadApi.getDownloadInfo({ downloadInfoDto: options, key: api.getKey() });
|
||||
downloadInfo = data;
|
||||
downloadInfo = await getDownloadInfo({ downloadInfoDto: options, key: getKey() });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to download files');
|
||||
return;
|
||||
|
@ -71,7 +72,7 @@ export const downloadArchive = async (fileName: string, options: DownloadInfoDto
|
|||
|
||||
try {
|
||||
const { data } = await api.downloadApi.downloadArchive(
|
||||
{ assetIdsDto: { assetIds: archive.assetIds }, key: api.getKey() },
|
||||
{ assetIdsDto: { assetIds: archive.assetIds }, key: getKey() },
|
||||
{
|
||||
responseType: 'blob',
|
||||
signal: abort.signal,
|
||||
|
@ -121,7 +122,7 @@ export const downloadFile = async (asset: AssetResponseDto) => {
|
|||
downloadManager.add(downloadKey, size, abort);
|
||||
|
||||
const { data } = await api.downloadApi.downloadFile(
|
||||
{ id, key: api.getKey() },
|
||||
{ id, key: getKey() },
|
||||
{
|
||||
responseType: 'blob',
|
||||
onDownloadProgress: ({ event }) => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { UploadState } from '$lib/models/upload-asset';
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { addAssetsToAlbum } from '$lib/utils/asset-utils';
|
||||
import { ExecutorQueue } from '$lib/utils/executor-queue';
|
||||
import { api, type AssetFileUploadResponseDto } from '@api';
|
||||
|
@ -81,7 +82,7 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
|
|||
isFavorite: false,
|
||||
duration: '0:00:00.000000',
|
||||
assetData: new File([asset], asset.name),
|
||||
key: api.getKey(),
|
||||
key: getKey(),
|
||||
},
|
||||
{
|
||||
onUploadProgress: ({ event }) => {
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { type SearchExploreResponseDto, api } from '@api';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import type { SearchExploreResponseDto } from '@immich/sdk';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
@ -58,7 +59,7 @@
|
|||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="100%"
|
||||
/>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
import { getAllPeople, getExploreData } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate();
|
||||
const { data: items } = await api.searchApi.getExploreData();
|
||||
const { data: response } = await api.personApi.getAllPeople({ withHidden: false });
|
||||
const [items, response] = await Promise.all([getExploreData(), getAllPeople({ withHidden: false })]);
|
||||
|
||||
return {
|
||||
items,
|
||||
response,
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import MapSettingsModal from '$lib/components/map-page/map-settings-modal.svelte';
|
||||
import Map from '$lib/components/shared-components/map/map.svelte';
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import type { MapSettings } from '$lib/stores/preferences.store';
|
||||
import { mapSettings } from '$lib/stores/preferences.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { type MapMarkerResponseDto, api } from '@api';
|
||||
import { type MapMarkerResponseDto } from '@api';
|
||||
import { getMapMarkers } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import Map from '$lib/components/shared-components/map/map.svelte';
|
||||
import type { MapSettings } from '$lib/stores/preferences.store';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -49,7 +50,7 @@
|
|||
const { includeArchived, onlyFavorites } = $mapSettings;
|
||||
const { fileCreatedAfter, fileCreatedBefore } = getFileCreatedDates();
|
||||
|
||||
const { data } = await api.assetApi.getMapMarkers(
|
||||
return await getMapMarkers(
|
||||
{
|
||||
isArchived: includeArchived && undefined,
|
||||
isFavorite: onlyFavorites || undefined,
|
||||
|
@ -60,7 +61,6 @@
|
|||
signal: abortController.signal,
|
||||
},
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
function getFileCreatedDates() {
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
<script lang="ts">
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import { api, type PeopleUpdateItem, type PersonResponseDto } from '@api';
|
||||
import { browser } from '$app/environment';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
||||
import SearchBar from '$lib/components/faces-page/search-bar.svelte';
|
||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
||||
import ShowHide from '$lib/components/faces-page/show-hide.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import {
|
||||
ActionQueryParameterValue,
|
||||
AppRoute,
|
||||
QueryParameter,
|
||||
maximumLengthSearchPeople,
|
||||
QueryParameter,
|
||||
timeBeforeShowLoadingSpinner,
|
||||
} from '$lib/constants';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import ShowHide from '$lib/components/faces-page/show-hide.svelte';
|
||||
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
|
||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
import { mdiAccountOff, mdiEyeOutline } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { searchNameLocal } from '$lib/utils/person';
|
||||
import SearchBar from '$lib/components/faces-page/search-bar.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
import { type PeopleUpdateItem, type PersonResponseDto } from '@api';
|
||||
import { getPerson, mergePerson, searchPerson, updatePeople, updatePerson } from '@immich/sdk';
|
||||
import { mdiAccountOff, mdiEyeOutline } from '@mdi/js';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -75,7 +77,7 @@
|
|||
const getSearchedPeople = $page.url.searchParams.get(QueryParameter.SEARCHED_PEOPLE);
|
||||
if (getSearchedPeople) {
|
||||
searchName = getSearchedPeople;
|
||||
searchPeople(true);
|
||||
handleSearchPeople(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -100,7 +102,7 @@
|
|||
const handleSearch = (force: boolean) => {
|
||||
$page.url.searchParams.set(QueryParameter.SEARCHED_PEOPLE, searchName);
|
||||
goto($page.url);
|
||||
searchPeople(force);
|
||||
handleSearchPeople(force);
|
||||
};
|
||||
|
||||
const handleCloseClick = () => {
|
||||
|
@ -150,7 +152,7 @@
|
|||
}
|
||||
|
||||
if (changed.length > 0) {
|
||||
const { data: results } = await api.personApi.updatePeople({
|
||||
const results = await updatePeople({
|
||||
peopleUpdateDto: { people: changed },
|
||||
});
|
||||
const count = results.filter(({ success }) => success).length;
|
||||
|
@ -187,12 +189,12 @@
|
|||
return;
|
||||
}
|
||||
try {
|
||||
await api.personApi.mergePerson({
|
||||
await mergePerson({
|
||||
id: personToBeMergedIn.id,
|
||||
mergePersonDto: { ids: [personToMerge.id] },
|
||||
});
|
||||
|
||||
const { data: mergedPerson } = await api.personApi.getPerson({ id: personToBeMergedIn.id });
|
||||
const mergedPerson = await getPerson({ id: personToBeMergedIn.id });
|
||||
|
||||
countVisiblePeople--;
|
||||
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
|
||||
|
@ -213,7 +215,7 @@
|
|||
*
|
||||
*/
|
||||
try {
|
||||
await api.personApi.updatePerson({ id: personToBeMergedIn.id, personUpdateDto: { name: personName } });
|
||||
await updatePerson({ id: personToBeMergedIn.id, personUpdateDto: { name: personName } });
|
||||
|
||||
for (const person of people) {
|
||||
if (person.id === personToBeMergedIn.id) {
|
||||
|
@ -248,7 +250,7 @@
|
|||
|
||||
const handleHidePerson = async (detail: PersonResponseDto) => {
|
||||
try {
|
||||
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||
const updatedPerson = await updatePerson({
|
||||
id: detail.id,
|
||||
personUpdateDto: { isHidden: true },
|
||||
});
|
||||
|
@ -281,7 +283,7 @@
|
|||
);
|
||||
};
|
||||
|
||||
const searchPeople = async (force: boolean) => {
|
||||
const handleSearchPeople = async (force: boolean) => {
|
||||
if (searchName === '') {
|
||||
if ($page.url.searchParams.has(QueryParameter.SEARCHED_PEOPLE)) {
|
||||
$page.url.searchParams.delete(QueryParameter.SEARCHED_PEOPLE);
|
||||
|
@ -295,9 +297,7 @@
|
|||
|
||||
const timeout = setTimeout(() => (isSearchingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { data } = await api.searchApi.searchPerson({ name: searchName, withHidden: false });
|
||||
|
||||
searchedPeople = data;
|
||||
searchedPeople = await searchPerson({ name: searchName, withHidden: false });
|
||||
searchWord = searchName;
|
||||
} catch (error) {
|
||||
handleError(error, "Can't search people");
|
||||
|
@ -318,7 +318,7 @@
|
|||
changeName();
|
||||
return;
|
||||
}
|
||||
const { data } = await api.searchApi.searchPerson({ name: personName, withHidden: true });
|
||||
const data = await searchPerson({ name: personName, withHidden: true });
|
||||
|
||||
// We check if another person has the same name as the name entered by the user
|
||||
|
||||
|
@ -353,7 +353,7 @@
|
|||
}
|
||||
|
||||
try {
|
||||
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||
const updatedPerson = await updatePerson({
|
||||
id: edittingPerson.id,
|
||||
personUpdateDto: { birthDate: value.length > 0 ? value : null },
|
||||
});
|
||||
|
@ -381,7 +381,7 @@
|
|||
return;
|
||||
}
|
||||
try {
|
||||
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||
const updatedPerson = await updatePerson({
|
||||
id: edittingPerson.id,
|
||||
personUpdateDto: { name: personName },
|
||||
});
|
||||
|
@ -529,7 +529,7 @@
|
|||
preload={searchName !== '' || index < 20}
|
||||
bind:hidden={person.isHidden}
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="100%"
|
||||
bind:eyeColor={eyeColorMap[person.id]}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
import { getAllPeople } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate();
|
||||
|
||||
const { data: people } = await api.personApi.getAllPeople({ withHidden: true });
|
||||
const people = await getAllPeople({ withHidden: true });
|
||||
return {
|
||||
people,
|
||||
meta: {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
||||
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
||||
import UnMergeFaceSelector from '$lib/components/faces-page/unmerge-face-selector.svelte';
|
||||
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
||||
import UnMergeFaceSelector from '$lib/components/faces-page/unmerge-face-selector.svelte';
|
||||
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
||||
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
|
||||
import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte';
|
||||
|
@ -21,24 +21,32 @@
|
|||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { AppRoute, QueryParameter, maximumLengthSearchPeople, timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { AssetStore } from '$lib/stores/assets.store';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { type AssetResponseDto, type PersonResponseDto, api } from '@api';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { mdiPlus, mdiDotsVertical, mdiArrowLeft } from '@mdi/js';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isExternalUrl } from '$lib/utils/navigation';
|
||||
import { searchNameLocal } from '$lib/utils/person';
|
||||
import {
|
||||
getPersonStatistics,
|
||||
mergePerson,
|
||||
searchPerson,
|
||||
updatePerson,
|
||||
type AssetResponseDto,
|
||||
type PersonResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { mdiArrowLeft, mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -74,7 +82,7 @@
|
|||
let refreshAssetGrid = false;
|
||||
|
||||
let personName = '';
|
||||
$: thumbnailData = api.getPeopleThumbnailUrl(data.person.id);
|
||||
$: thumbnailData = getPeopleThumbnailUrl(data.person.id);
|
||||
|
||||
let name: string = data.person.name;
|
||||
let suggestedPeople: PersonResponseDto[] = [];
|
||||
|
@ -97,8 +105,7 @@
|
|||
}
|
||||
const timeout = setTimeout(() => (isSearchingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { data } = await api.searchApi.searchPerson({ name });
|
||||
people = data;
|
||||
people = await searchPerson({ name });
|
||||
searchWord = name;
|
||||
} catch (error) {
|
||||
people = [];
|
||||
|
@ -113,7 +120,7 @@
|
|||
$: isAllArchive = [...$selectedAssets].every((asset) => asset.isArchived);
|
||||
$: isAllFavorite = [...$selectedAssets].every((asset) => asset.isFavorite);
|
||||
$: $onPersonThumbnail === data.person.id &&
|
||||
(thumbnailData = api.getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`);
|
||||
(thumbnailData = getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`);
|
||||
|
||||
$: {
|
||||
if (people) {
|
||||
|
@ -215,7 +222,7 @@
|
|||
|
||||
const toggleHidePerson = async () => {
|
||||
try {
|
||||
await api.personApi.updatePerson({
|
||||
await updatePerson({
|
||||
id: data.person.id,
|
||||
personUpdateDto: { isHidden: !data.person.isHidden },
|
||||
});
|
||||
|
@ -232,8 +239,8 @@
|
|||
};
|
||||
|
||||
const handleMerge = async (person: PersonResponseDto) => {
|
||||
const { data: statistics } = await api.personApi.getPersonStatistics({ id: person.id });
|
||||
numberOfAssets = statistics.assets;
|
||||
const { assets } = await getPersonStatistics({ id: person.id });
|
||||
numberOfAssets = assets;
|
||||
handleGoBack();
|
||||
|
||||
data.person = person;
|
||||
|
@ -246,7 +253,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
await api.personApi.updatePerson({ id: data.person.id, personUpdateDto: { featureFaceAssetId: asset.id } });
|
||||
await updatePerson({ id: data.person.id, personUpdateDto: { featureFaceAssetId: asset.id } });
|
||||
|
||||
notificationController.show({ message: 'Feature photo updated', type: NotificationType.Info });
|
||||
assetInteractionStore.clearMultiselect();
|
||||
|
@ -256,10 +263,8 @@
|
|||
|
||||
const updateAssetCount = async () => {
|
||||
try {
|
||||
const { data: statistics } = await api.personApi.getPersonStatistics({
|
||||
id: data.person.id,
|
||||
});
|
||||
numberOfAssets = statistics.assets;
|
||||
const { assets } = await getPersonStatistics({ id: data.person.id });
|
||||
numberOfAssets = assets;
|
||||
} catch (error) {
|
||||
handleError(error, "Can't update the asset count");
|
||||
}
|
||||
|
@ -270,7 +275,7 @@
|
|||
viewMode = ViewMode.VIEW_ASSETS;
|
||||
isEditingName = false;
|
||||
try {
|
||||
await api.personApi.mergePerson({
|
||||
await mergePerson({
|
||||
id: personToBeMergedIn.id,
|
||||
mergePersonDto: { ids: [personToMerge.id] },
|
||||
});
|
||||
|
@ -305,10 +310,7 @@
|
|||
try {
|
||||
isEditingName = false;
|
||||
|
||||
await api.personApi.updatePerson({
|
||||
id: data.person.id,
|
||||
personUpdateDto: { name: personName },
|
||||
});
|
||||
await updatePerson({ id: data.person.id, personUpdateDto: { name: personName } });
|
||||
|
||||
notificationController.show({
|
||||
message: 'Change name successfully',
|
||||
|
@ -340,16 +342,16 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const result = await api.searchApi.searchPerson({ name: personName, withHidden: true });
|
||||
const result = await searchPerson({ name: personName, withHidden: true });
|
||||
|
||||
const existingPerson = result.data.find(
|
||||
const existingPerson = result.find(
|
||||
(person: PersonResponseDto) =>
|
||||
person.name.toLowerCase() === personName.toLowerCase() && person.id !== data.person.id && person.name,
|
||||
);
|
||||
if (existingPerson) {
|
||||
personMerge2 = existingPerson;
|
||||
personMerge1 = data.person;
|
||||
potentialMergePeople = result.data
|
||||
potentialMergePeople = result
|
||||
.filter(
|
||||
(person: PersonResponseDto) =>
|
||||
personMerge2.name.toLowerCase() === person.name.toLowerCase() &&
|
||||
|
@ -369,7 +371,7 @@
|
|||
viewMode = ViewMode.VIEW_ASSETS;
|
||||
data.person.birthDate = birthDate;
|
||||
|
||||
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||
const updatedPerson = await updatePerson({
|
||||
id: data.person.id,
|
||||
personUpdateDto: { birthDate: birthDate.length > 0 ? birthDate : null },
|
||||
});
|
||||
|
@ -557,7 +559,7 @@
|
|||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
url={getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="2rem"
|
||||
heightStyle="2rem"
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
import { getPerson, getPersonStatistics } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ params }) => {
|
||||
await authenticate();
|
||||
|
||||
const { data: person } = await api.personApi.getPerson({ id: params.personId });
|
||||
const { data: statistics } = await api.personApi.getPersonStatistics({ id: params.personId });
|
||||
const [person, statistics] = await Promise.all([
|
||||
getPerson({ id: params.personId }),
|
||||
getPersonStatistics({ id: params.personId }),
|
||||
]);
|
||||
|
||||
return {
|
||||
person,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
import { getExploreData } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate();
|
||||
const { data: items } = await api.searchApi.getExploreData();
|
||||
const items = await getExploreData();
|
||||
|
||||
return {
|
||||
items,
|
||||
|
|
|
@ -26,9 +26,8 @@
|
|||
import { preventRaceConditionSearchBar } from '$lib/stores/search.store';
|
||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||
import { mdiArrowLeft, mdiDotsVertical, mdiImageOffOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
|
||||
import type { AssetResponseDto, SearchResponseDto } from '@immich/sdk';
|
||||
import { search, type AssetResponseDto, type SearchResponseDto } from '@immich/sdk';
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -120,20 +119,20 @@
|
|||
await authenticate();
|
||||
let results: SearchResponseDto | null = null;
|
||||
$page.url.searchParams.set('page', curPage.toString());
|
||||
const res = await api.searchApi.search({}, { params: $page.url.searchParams });
|
||||
const res = await search({ ...$page.url.searchParams });
|
||||
if (searchResultAssets) {
|
||||
searchResultAssets.push(...res.data.assets.items);
|
||||
searchResultAssets.push(...res.assets.items);
|
||||
} else {
|
||||
searchResultAssets = res.data.assets.items;
|
||||
searchResultAssets = res.assets.items;
|
||||
}
|
||||
|
||||
const assets = {
|
||||
...res.data.assets,
|
||||
...res.assets,
|
||||
items: searchResultAssets,
|
||||
};
|
||||
results = {
|
||||
assets,
|
||||
albums: res.data.albums,
|
||||
albums: res.albums,
|
||||
};
|
||||
|
||||
data.results = results;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { type AssetResponseDto, type SearchResponseDto, api } from '@api';
|
||||
import { search, type AssetResponseDto, type SearchResponseDto } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
import { QueryParameter } from '$lib/constants';
|
||||
|
||||
|
@ -10,17 +10,17 @@ export const load = (async (data) => {
|
|||
url.searchParams.get(QueryParameter.SEARCH_TERM) || url.searchParams.get(QueryParameter.QUERY) || undefined;
|
||||
let results: SearchResponseDto | null = null;
|
||||
if (term) {
|
||||
const res = await api.searchApi.search({}, { params: data.url.searchParams });
|
||||
const response = await search({ ...data.url.searchParams });
|
||||
let items: AssetResponseDto[] = (data as unknown as { results: SearchResponseDto }).results?.assets.items;
|
||||
if (items) {
|
||||
items.push(...res.data.assets.items);
|
||||
items.push(...response.assets.items);
|
||||
} else {
|
||||
items = res.data.assets.items;
|
||||
items = response.assets.items;
|
||||
}
|
||||
const assets = { ...res.data.assets, items };
|
||||
const assets = { ...response.assets, items };
|
||||
results = {
|
||||
assets,
|
||||
albums: res.data.albums,
|
||||
albums: response.albums,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<script lang="ts">
|
||||
import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import IndividualSharedViewer from '$lib/components/share-page/individual-shared-viewer.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||
import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import { api, SharedLinkType } from '@api';
|
||||
import type { PageData } from './$types';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { SharedLinkType } from '@api';
|
||||
import { getMySharedLink } from '@immich/sdk';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
let { sharedLink, passwordRequired, sharedLinkKey: key, meta } = data;
|
||||
|
@ -18,9 +19,8 @@
|
|||
|
||||
const handlePasswordSubmit = async () => {
|
||||
try {
|
||||
const result = await api.sharedLinkApi.getMySharedLink({ password, key });
|
||||
sharedLink = await getMySharedLink({ password, key });
|
||||
passwordRequired = false;
|
||||
sharedLink = result.data;
|
||||
isOwned = $user ? $user.id === sharedLink.userId : false;
|
||||
title = (sharedLink.album ? sharedLink.album.albumName : 'Public Share') + ' - Immich';
|
||||
description = sharedLink.description || `${sharedLink.assets.length} shared photos & videos.`;
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { api, ThumbnailFormat } from '@api';
|
||||
import { ThumbnailFormat } from '@api';
|
||||
import { getMySharedLink } from '@immich/sdk';
|
||||
import { error as throwError } from '@sveltejs/kit';
|
||||
import type { AxiosError } from 'axios';
|
||||
import type { PageLoad } from './$types';
|
||||
import { error as throwError } from '@sveltejs/kit';
|
||||
|
||||
export const load = (async ({ params }) => {
|
||||
const { key } = params;
|
||||
await authenticate({ public: true });
|
||||
|
||||
try {
|
||||
const { data: sharedLink } = await api.sharedLinkApi.getMySharedLink({ key });
|
||||
|
||||
const sharedLink = await getMySharedLink({ key });
|
||||
const assetCount = sharedLink.assets.length;
|
||||
const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
|
||||
|
||||
|
@ -19,9 +20,7 @@ export const load = (async ({ params }) => {
|
|||
meta: {
|
||||
title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
|
||||
description: sharedLink.description || `${assetCount} shared photos & videos.`,
|
||||
imageUrl: assetId
|
||||
? api.getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
|
||||
: '/feature-panel.png',
|
||||
imageUrl: assetId ? getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp) : '/feature-panel.png',
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { api } from '@api';
|
||||
import { getAssetInfo } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ params }) => {
|
||||
const { key, assetId } = params;
|
||||
const { data: asset } = await api.assetApi.getAssetInfo({ id: assetId, key });
|
||||
const asset = await getAssetInfo({ id: assetId, key });
|
||||
|
||||
return {
|
||||
asset,
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
<script lang="ts">
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import { api, copyToClipboard, makeSharedLinkUrl, type SharedLinkResponseDto } from '@api';
|
||||
import { goto } from '$app/navigation';
|
||||
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { onMount } from 'svelte';
|
||||
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { mdiArrowLeft } from '@mdi/js';
|
||||
import { serverConfig } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { copyToClipboard, makeSharedLinkUrl, type SharedLinkResponseDto } from '@api';
|
||||
import { getAllSharedLinks, removeSharedLink } from '@immich/sdk';
|
||||
import { mdiArrowLeft } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let sharedLinks: SharedLinkResponseDto[] = [];
|
||||
let editSharedLink: SharedLinkResponseDto | null = null;
|
||||
|
@ -21,8 +22,7 @@
|
|||
let deleteLinkId: string | null = null;
|
||||
|
||||
const refresh = async () => {
|
||||
const { data } = await api.sharedLinkApi.getAllSharedLinks();
|
||||
sharedLinks = data;
|
||||
sharedLinks = await getAllSharedLinks();
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
try {
|
||||
await api.sharedLinkApi.removeSharedLink({ id: deleteLinkId });
|
||||
await removeSharedLink({ id: deleteLinkId });
|
||||
notificationController.show({ message: 'Deleted shared link', type: NotificationType.Info });
|
||||
deleteLinkId = null;
|
||||
await refresh();
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { page } from '$app/stores';
|
||||
import { afterNavigate, beforeNavigate } from '$app/navigation';
|
||||
import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import DownloadPanel from '$lib/components/asset-viewer/download-panel.svelte';
|
||||
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
||||
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
|
||||
import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import AppleHeader from '$lib/components/shared-components/apple-header.svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { loadConfig } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api } from '@api';
|
||||
import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { type ThemeSetting, colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
|
||||
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
|
||||
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
||||
import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte';
|
||||
import { Theme } from '$lib/constants';
|
||||
import { colorTheme, handleToggleTheme, type ThemeSetting } from '$lib/stores/preferences.store';
|
||||
import { loadConfig } from '$lib/stores/server-config.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';
|
||||
import { setKey } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import '../app.css';
|
||||
|
||||
let showNavigationLoadingBar = false;
|
||||
let albumId: string | undefined;
|
||||
|
@ -55,7 +55,7 @@
|
|||
});
|
||||
|
||||
if (isSharedLinkRoute($page.route?.id)) {
|
||||
api.setKey($page.params.key);
|
||||
setKey($page.params.key);
|
||||
}
|
||||
|
||||
beforeNavigate(({ from, to }) => {
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { type AllJobStatusResponseDto } from '@api';
|
||||
import { getAllJobsStatus } from '@immich/sdk';
|
||||
import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk';
|
||||
import { mdiCog } from '@mdi/js';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { AlbumResponseDto } from '@api';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { AlbumResponseDto } from '@immich/sdk';
|
||||
import { Sync } from 'factory.ts';
|
||||
import { userFactory } from './user-factory';
|
||||
|
||||
|
|
Loading…
Reference in a new issue