feat: toasts (#23298)

This commit is contained in:
Jason Rasmussen
2025-10-28 15:09:11 -04:00
committed by GitHub
parent 106effca2e
commit 52596255c8
80 changed files with 341 additions and 1069 deletions

View File

@@ -14,11 +14,10 @@
type AlbumResponseDto,
type UserResponseDto,
} from '@immich/sdk';
import { Icon, Modal, ModalBody, modalManager } from '@immich/ui';
import { Icon, Modal, ModalBody, modalManager, toastManager } from '@immich/ui';
import { mdiArrowDownThin, mdiArrowUpThin, mdiDotsVertical, mdiPlus } from '@mdi/js';
import { findKey } from 'lodash-es';
import { t } from 'svelte-i18n';
import { notificationController, NotificationType } from '../components/shared-components/notification/notification';
import SettingDropdown from '../components/shared-components/settings/setting-dropdown.svelte';
interface Props {
@@ -68,10 +67,7 @@
},
});
notificationController.show({
type: NotificationType.Info,
message: $t('activity_changed', { values: { enabled: album.isActivityEnabled } }),
});
toastManager.success($t('activity_changed', { values: { enabled: album.isActivityEnabled } }));
} catch (error) {
handleError(error, $t('errors.cant_change_activity', { values: { enabled: album.isActivityEnabled } }));
}
@@ -91,10 +87,7 @@
try {
await removeUserFromAlbum({ id: album.id, userId: user.id });
onClose({ action: 'refreshAlbum' });
notificationController.show({
type: NotificationType.Info,
message: $t('album_user_removed', { values: { user: user.name } }),
});
toastManager.success($t('album_user_removed', { values: { user: user.name } }));
} catch (error) {
handleError(error, $t('errors.unable_to_remove_album_users'));
}
@@ -107,7 +100,7 @@
values: { user: user.name, role: role == AlbumUserRole.Viewer ? $t('role_viewer') : $t('role_editor') },
});
onClose({ action: 'refreshAlbum' });
notificationController.show({ type: NotificationType.Info, message });
toastManager.success(message);
} catch (error) {
handleError(error, $t('errors.unable_to_change_album_user_role'));
}

View File

@@ -1,10 +1,6 @@
<script lang="ts">
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import {
NotificationType,
notificationController,
} from '$lib/components/shared-components/notification/notification';
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
import { handleError } from '$lib/utils/handle-error';
import {
@@ -15,7 +11,7 @@
type AlbumResponseDto,
type UserResponseDto,
} from '@immich/sdk';
import { Button, Modal, ModalBody, Text, modalManager } from '@immich/ui';
import { Button, Modal, ModalBody, Text, modalManager, toastManager } from '@immich/ui';
import { mdiDotsVertical } from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
@@ -80,21 +76,20 @@
userId === 'me'
? $t('album_user_left', { values: { album: album.albumName } })
: $t('album_user_removed', { values: { user: user.name } });
notificationController.show({ type: NotificationType.Info, message });
toastManager.success(message);
onClose(true);
} catch (error) {
handleError(error, $t('errors.unable_to_remove_album_users'));
}
};
const handleSetReadonly = async (user: UserResponseDto, role: AlbumUserRole) => {
const handleChangeRole = async (user: UserResponseDto, role: AlbumUserRole) => {
try {
await updateAlbumUser({ id: album.id, userId: user.id, updateAlbumUserDto: { role } });
const message = $t('user_role_set', {
values: { user: user.name, role: role == AlbumUserRole.Viewer ? $t('role_viewer') : $t('role_editor') },
});
notificationController.show({ type: NotificationType.Info, message });
toastManager.success(message);
onClose(true);
} catch (error) {
handleError(error, $t('errors.unable_to_change_album_user_role'));
@@ -131,10 +126,10 @@
{#if isOwned}
<ButtonContextMenu icon={mdiDotsVertical} size="medium" title={$t('options')}>
{#if role === AlbumUserRole.Viewer}
<MenuOption onClick={() => handleSetReadonly(user, AlbumUserRole.Editor)} text={$t('allow_edits')} />
<MenuOption onClick={() => handleChangeRole(user, AlbumUserRole.Editor)} text={$t('allow_edits')} />
{:else}
<MenuOption
onClick={() => handleSetReadonly(user, AlbumUserRole.Viewer)}
onClick={() => handleChangeRole(user, AlbumUserRole.Viewer)}
text={$t('disallow_edits')}
/>
{/if}

View File

@@ -1,11 +1,19 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import ApiKeyGrid from '$lib/components/user-settings-page/user-api-key-grid.svelte';
import { Permission } from '@immich/sdk';
import { Button, Checkbox, Field, HStack, IconButton, Input, Label, Modal, ModalBody, ModalFooter } from '@immich/ui';
import {
Button,
Checkbox,
Field,
HStack,
IconButton,
Input,
Label,
Modal,
ModalBody,
ModalFooter,
toastManager,
} from '@immich/ui';
import { mdiClose, mdiKeyVariant } from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
@@ -60,16 +68,10 @@
};
const handleSubmit = () => {
if (!apiKey.name) {
notificationController.show({
message: $t('api_key_empty'),
type: NotificationType.Warning,
});
if (!name) {
toastManager.warning($t('api_key_empty'));
} else if (selectedItems.length === 0) {
notificationController.show({
message: $t('permission_empty'),
type: NotificationType.Warning,
});
toastManager.warning($t('permission_empty'));
} else {
if (selectAllItems) {
onClose({ name, permissions: [Permission.All] });

View File

@@ -1,13 +1,9 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
import { user } from '$lib/stores/user.store';
import { handleError } from '$lib/utils/handle-error';
import { deleteProfileImage, updateMyUser, UserAvatarColor } from '@immich/sdk';
import { Modal, ModalBody } from '@immich/ui';
import { Modal, ModalBody, toastManager } from '@immich/ui';
import { t } from 'svelte-i18n';
interface Props {
@@ -24,7 +20,7 @@
await deleteProfileImage();
}
notificationController.show({ message: $t('saved_profile'), type: NotificationType.Info });
toastManager.success($t('saved_profile'));
$user = await updateMyUser({ userUpdateMeDto: { avatarColor: color } });
onClose();

View File

@@ -1,12 +1,8 @@
<script lang="ts">
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { createJob, ManualJobName } from '@immich/sdk';
import { ConfirmModal } from '@immich/ui';
import { ConfirmModal, toastManager } from '@immich/ui';
import { t } from 'svelte-i18n';
type Props = { onClose: (confirmed: boolean) => void };
@@ -36,7 +32,7 @@
try {
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
notificationController.show({ message: $t('admin.job_created'), type: NotificationType.Info });
toastManager.success($t('admin.job_created'));
onClose(true);
} catch (error) {
handleError(error, $t('errors.unable_to_submit_job'));

View File

@@ -1,12 +1,8 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import DateInput from '$lib/elements/DateInput.svelte';
import { handleError } from '$lib/utils/handle-error';
import { updatePerson, type PersonResponseDto } from '@immich/sdk';
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
import { Button, HStack, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
import { mdiCake } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -27,7 +23,7 @@
personUpdateDto: { birthDate },
});
notificationController.show({ message: $t('date_of_birth_saved'), type: NotificationType.Info });
toastManager.success($t('date_of_birth_saved'));
onClose(updatedPerson);
} catch (error) {
handleError(error, $t('errors.unable_to_save_date_of_birth'));

View File

@@ -1,12 +1,8 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { getPeopleThumbnailUrl } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { mergePerson, type PersonResponseDto } from '@immich/sdk';
import { Button, HStack, Icon, IconButton, Modal, ModalBody, ModalFooter } from '@immich/ui';
import { Button, HStack, Icon, IconButton, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
import { mdiArrowLeft, mdiCallMerge, mdiSwapHorizontal } from '@mdi/js';
import { onMount, tick } from 'svelte';
import { t } from 'svelte-i18n';
@@ -42,11 +38,7 @@
id: personToBeMergedInto.id,
mergePersonDto: { ids: [personToMerge.id] },
});
notificationController.show({
message: $t('merge_people_successfully'),
type: NotificationType.Info,
});
toastManager.success($t('merge_people_successfully'));
onClose([personToMerge, personToBeMergedInto]);
} catch (error) {
handleError(error, $t('errors.unable_to_save_name'));

View File

@@ -1,8 +1,4 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { featureFlags } from '$lib/stores/server-config.store';
import { handleError } from '$lib/utils/handle-error';
import { resetPinCode } from '@immich/sdk';
@@ -17,6 +13,7 @@
PasswordInput,
Stack,
Text,
toastManager,
} from '@immich/ui';
import { mdiLockReset } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -33,7 +30,7 @@
const handleReset = async () => {
try {
await resetPinCode({ pinCodeResetDto: { password } });
notificationController.show({ message: $t('pin_code_reset_successfully'), type: NotificationType.Info });
toastManager.success($t('pin_code_reset_successfully'));
onClose(true);
} catch (error) {
handleError(error, $t('errors.failed_to_reset_pin_code'));

View File

@@ -2,12 +2,11 @@
import { user } from '$lib/stores/user.store';
import { handleError } from '$lib/utils/handle-error';
import { createProfileImage, type AssetResponseDto } from '@immich/sdk';
import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui';
import { Button, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
import domtoimage from 'dom-to-image';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import PhotoViewer from '../components/asset-viewer/photo-viewer.svelte';
import { NotificationType, notificationController } from '../components/shared-components/notification/notification';
interface Props {
asset: AssetResponseDto;
@@ -65,20 +64,12 @@
});
if (await hasTransparentPixels(blob)) {
notificationController.show({
type: NotificationType.Error,
message: $t('errors.profile_picture_transparent_pixels'),
timeout: 3000,
});
toastManager.danger($t('errors.profile_picture_transparent_pixels'));
return;
}
const file = new File([blob], 'profile-picture.png', { type: 'image/png' });
const { profileImagePath, profileChangedAt } = await createProfileImage({ createProfileImageDto: { file } });
notificationController.show({
type: NotificationType.Info,
message: $t('profile_picture_set'),
timeout: 3000,
});
toastManager.success($t('profile_picture_set'));
$user.profileImagePath = profileImagePath;
$user.profileChangedAt = profileChangedAt;
} catch (error) {

View File

@@ -3,11 +3,21 @@
import { locale } from '$lib/stores/preferences.store';
import { handleError } from '$lib/utils/handle-error';
import { SharedLinkType, createSharedLink, updateSharedLink, type SharedLinkResponseDto } from '@immich/sdk';
import { Button, Field, Input, Modal, ModalBody, ModalFooter, PasswordInput, Switch, Text } from '@immich/ui';
import {
Button,
Field,
Input,
Modal,
ModalBody,
ModalFooter,
PasswordInput,
Switch,
Text,
toastManager,
} from '@immich/ui';
import { mdiLink } from '@mdi/js';
import { DateTime, Duration } from 'luxon';
import { t } from 'svelte-i18n';
import { NotificationType, notificationController } from '../components/shared-components/notification/notification';
interface Props {
onClose: (sharedLink?: SharedLinkResponseDto) => void;
@@ -116,10 +126,7 @@
},
});
notificationController.show({
type: NotificationType.Info,
message: $t('edited'),
});
toastManager.success($t('saved'));
onClose(updatedLink);
} catch (error) {

View File

@@ -1,13 +1,9 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
import { SettingInputFieldType } from '$lib/constants';
import type { TreeNode } from '$lib/utils/tree-utils';
import { upsertTags, type TagResponseDto } from '@immich/sdk';
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
import { Button, HStack, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
import { mdiTag } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -27,10 +23,7 @@
return;
}
notificationController.show({
message: $t('tag_created', { values: { tag: tag.value } }),
type: NotificationType.Info,
});
toastManager.success($t('tag_created', { values: { tag: tag.value } }));
onClose(tag);
};

View File

@@ -1,13 +1,9 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
import { SettingInputFieldType } from '$lib/constants';
import type { TreeNode } from '$lib/utils/tree-utils';
import { updateTag, type TagResponseDto } from '@immich/sdk';
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
import { Button, HStack, Modal, ModalBody, ModalFooter, toastManager } from '@immich/ui';
import { mdiTag } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -27,10 +23,7 @@
const updatedTag = await updateTag({ id: tag.id, tagUpdateDto: { color: tagColor } });
notificationController.show({
message: $t('tag_updated', { values: { tag: tag.value } }),
type: NotificationType.Info,
});
toastManager.success($t('tag_updated', { values: { tag: tag.value } }));
onClose(updatedTag);
};