mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
feat(web): s (#9663)
This commit is contained in:
parent
ae21781442
commit
f6f82a5662
10 changed files with 25 additions and 22 deletions
|
@ -7,6 +7,7 @@
|
||||||
import { getShortDateRange } from '$lib/utils/date-time';
|
import { getShortDateRange } from '$lib/utils/date-time';
|
||||||
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
||||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
export let album: AlbumResponseDto;
|
export let album: AlbumResponseDto;
|
||||||
export let showOwner = false;
|
export let showOwner = false;
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
{#if showItemCount}
|
{#if showItemCount}
|
||||||
<p>
|
<p>
|
||||||
{album.assetCount.toLocaleString($locale)}
|
{album.assetCount.toLocaleString($locale)}
|
||||||
{album.assetCount === 1 ? `item` : `items`}
|
item{s(album.assetCount)}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
import FaceThumbnail from './face-thumbnail.svelte';
|
import FaceThumbnail from './face-thumbnail.svelte';
|
||||||
import PeopleList from './people-list.svelte';
|
import PeopleList from './people-list.svelte';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
export let assetIds: string[];
|
export let assetIds: string[];
|
||||||
export let personAssets: PersonResponseDto;
|
export let personAssets: PersonResponseDto;
|
||||||
|
@ -76,7 +77,7 @@
|
||||||
await reassignFaces({ id: data.id, assetFaceUpdateDto: { data: selectedPeople } });
|
await reassignFaces({ id: data.id, assetFaceUpdateDto: { data: selectedPeople } });
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to a new person`,
|
message: `Re-assigned ${assetIds.length} asset${s(assetIds.length)} to a new person`,
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -96,7 +97,7 @@
|
||||||
if (selectedPerson) {
|
if (selectedPerson) {
|
||||||
await reassignFaces({ id: selectedPerson.id, assetFaceUpdateDto: { data: selectedPeople } });
|
await reassignFaces({ id: selectedPerson.id, assetFaceUpdateDto: { data: selectedPeople } });
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Re-assigned ${assetIds.length} asset${assetIds.length > 1 ? 's' : ''} to ${
|
message: `Re-assigned ${assetIds.length} asset${s(assetIds.length)} to ${
|
||||||
selectedPerson.name || 'an existing person'
|
selectedPerson.name || 'an existing person'
|
||||||
}`,
|
}`,
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
|
import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
|
||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
export let library: LibraryResponseDto;
|
export let library: LibraryResponseDto;
|
||||||
|
|
||||||
|
@ -56,14 +57,9 @@
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (failedPaths === 1) {
|
|
||||||
notificationController.show({
|
|
||||||
message: `${failedPaths} path failed validation`,
|
|
||||||
type: NotificationType.Warning,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `${failedPaths} paths failed validation`,
|
message: `${failedPaths} path${s(failedPaths)} failed validation`,
|
||||||
type: NotificationType.Warning,
|
type: NotificationType.Warning,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
|
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
|
||||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
export let album: AlbumResponseDto;
|
export let album: AlbumResponseDto;
|
||||||
export let onRemove: ((assetIds: string[]) => void) | undefined;
|
export let onRemove: ((assetIds: string[]) => void) | undefined;
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
const count = results.filter(({ success }) => success).length;
|
const count = results.filter(({ success }) => success).length;
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
message: `Removed ${count} asset${count === 1 ? '' : 's'}`,
|
message: `Removed ${count} asset${s(count)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
clearSelect();
|
clearSelect();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import Checkbox from '$lib/components/elements/checkbox.svelte';
|
import Checkbox from '$lib/components/elements/checkbox.svelte';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
export let size: number;
|
export let size: number;
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
|
|
||||||
<ConfirmDialogue
|
<ConfirmDialogue
|
||||||
id="permanently-delete-asset-modal"
|
id="permanently-delete-asset-modal"
|
||||||
title="Permanently delete asset{size > 1 ? 's' : ''}"
|
title="Permanently delete asset{s(size)}"
|
||||||
confirmText="Delete"
|
confirmText="Delete"
|
||||||
onConfirm={handleConfirm}
|
onConfirm={handleConfirm}
|
||||||
onClose={() => dispatch('cancel')}
|
onClose={() => dispatch('cancel')}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import { uploadExecutionQueue } from '$lib/utils/file-uploader';
|
import { uploadExecutionQueue } from '$lib/utils/file-uploader';
|
||||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||||
import { mdiCog, mdiWindowMinimize, mdiCancel, mdiCloudUploadOutline } from '@mdi/js';
|
import { mdiCog, mdiWindowMinimize, mdiCancel, mdiCloudUploadOutline } from '@mdi/js';
|
||||||
|
import { s } from '$lib/utils';
|
||||||
|
|
||||||
let showDetail = false;
|
let showDetail = false;
|
||||||
let showOptions = false;
|
let showOptions = false;
|
||||||
|
@ -36,7 +37,7 @@
|
||||||
on:outroend={() => {
|
on:outroend={() => {
|
||||||
if ($errorCounter > 0) {
|
if ($errorCounter > 0) {
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Upload completed with ${$errorCounter} error${$errorCounter > 1 ? 's' : ''}, refresh the page to see new upload assets.`,
|
message: `Upload completed with ${$errorCounter} error${s($errorCounter)}, refresh the page to see new upload assets.`,
|
||||||
type: NotificationType.Warning,
|
type: NotificationType.Warning,
|
||||||
});
|
});
|
||||||
} else if ($successCounter > 0) {
|
} else if ($successCounter > 0) {
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
}
|
}
|
||||||
if ($duplicateCounter > 0) {
|
if ($duplicateCounter > 0) {
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Skipped ${$duplicateCounter} duplicate asset${$duplicateCounter > 1 ? 's' : ''}`,
|
message: `Skipped ${$duplicateCounter} duplicate asset${s($duplicateCounter)}`,
|
||||||
type: NotificationType.Warning,
|
type: NotificationType.Warning,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,4 +279,6 @@ export const handlePromiseError = <T>(promise: Promise<T>): void => {
|
||||||
promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
|
promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const memoryLaneTitle = (yearsAgo: number) => `${yearsAgo} ${yearsAgo ? 'years' : 'year'} ago`;
|
export const s = (count: number) => (count === 1 ? '' : 's');
|
||||||
|
|
||||||
|
export const memoryLaneTitle = (yearsAgo: number) => `year${s(yearsAgo)} ago`;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store'
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { BucketPosition, isSelectingAllAssets, type AssetStore } from '$lib/stores/assets.store';
|
import { BucketPosition, isSelectingAllAssets, type AssetStore } from '$lib/stores/assets.store';
|
||||||
import { downloadManager } from '$lib/stores/download';
|
import { downloadManager } from '$lib/stores/download';
|
||||||
import { downloadRequest, getKey } from '$lib/utils';
|
import { downloadRequest, getKey, s } from '$lib/utils';
|
||||||
import { createAlbum } from '$lib/utils/album-utils';
|
import { createAlbum } from '$lib/utils/album-utils';
|
||||||
import { encodeHTMLSpecialChars } from '$lib/utils/string-utils';
|
import { encodeHTMLSpecialChars } from '$lib/utils/string-utils';
|
||||||
import {
|
import {
|
||||||
|
@ -38,7 +38,7 @@ export const addAssetsToAlbum = async (albumId: string, assetIds: string[]) => {
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
message:
|
message:
|
||||||
count > 0
|
count > 0
|
||||||
? `Added ${count} asset${count === 1 ? '' : 's'} to the album`
|
? `Added ${count} asset${s(count)} to the album`
|
||||||
: `Asset${assetIds.length === 1 ? ' was' : 's were'} already part of the album`,
|
: `Asset${assetIds.length === 1 ? ' was' : 's were'} already part of the album`,
|
||||||
button: {
|
button: {
|
||||||
text: 'View Album',
|
text: 'View Album',
|
||||||
|
@ -58,7 +58,7 @@ export const addAssetsToNewAlbum = async (albumName: string, assetIds: string[])
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
message: `Added ${assetIds.length} asset${assetIds.length === 1 ? '' : 's'} to ${displayName}`,
|
message: `Added ${assetIds.length} asset${s(assetIds.length)} to ${displayName}`,
|
||||||
html: true,
|
html: true,
|
||||||
button: {
|
button: {
|
||||||
text: 'View Album',
|
text: 'View Album',
|
||||||
|
@ -267,7 +267,7 @@ export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserRespo
|
||||||
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
||||||
if (numberOfIssues > 0) {
|
if (numberOfIssues > 0) {
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`,
|
message: `Can't change metadata of ${numberOfIssues} asset${s(numberOfIssues)}`,
|
||||||
type: NotificationType.Warning,
|
type: NotificationType.Warning,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError, s } from '$lib/utils';
|
||||||
import { downloadAlbum } from '$lib/utils/asset-utils';
|
import { downloadAlbum } from '$lib/utils/asset-utils';
|
||||||
import { clickOutside } from '$lib/utils/click-outside';
|
import { clickOutside } from '$lib/utils/click-outside';
|
||||||
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
|
@ -291,7 +291,7 @@
|
||||||
const count = results.filter(({ success }) => success).length;
|
const count = results.filter(({ success }) => success).length;
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
message: `Added ${count} asset${count === 1 ? '' : 's'}`,
|
message: `Added ${count} asset${s(count)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
await refreshAlbum();
|
await refreshAlbum();
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { AssetStore } from '$lib/stores/assets.store';
|
import { AssetStore } from '$lib/stores/assets.store';
|
||||||
import { websocketEvents } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
|
import { getPeopleThumbnailUrl, handlePromiseError, s } from '$lib/utils';
|
||||||
import { clickOutside } from '$lib/utils/click-outside';
|
import { clickOutside } from '$lib/utils/click-outside';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { isExternalUrl } from '$lib/utils/navigation';
|
import { isExternalUrl } from '$lib/utils/navigation';
|
||||||
|
@ -482,7 +482,7 @@
|
||||||
{#if data.person.name}
|
{#if data.person.name}
|
||||||
<p class="w-40 sm:w-72 font-medium truncate">{data.person.name}</p>
|
<p class="w-40 sm:w-72 font-medium truncate">{data.person.name}</p>
|
||||||
<p class="absolute w-fit text-sm text-gray-500 dark:text-immich-gray bottom-0">
|
<p class="absolute w-fit text-sm text-gray-500 dark:text-immich-gray bottom-0">
|
||||||
{`${numberOfAssets} asset${numberOfAssets > 1 ? 's' : ''}`}
|
{`${numberOfAssets} asset${s(numberOfAssets)}`}
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="font-medium">Add a name</p>
|
<p class="font-medium">Add a name</p>
|
||||||
|
|
Loading…
Reference in a new issue