1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-24 08:52:28 +02:00

chore(web): unique ID generation (#9932)

* chore(web): automatically generate unique IDs

* fix: revert changes to Slider

* chore: add test for id store
This commit is contained in:
Ben 2024-06-01 22:58:35 +00:00 committed by GitHub
parent 4e16e2520d
commit 01f52c9021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 83 additions and 150 deletions

View File

@ -43,7 +43,6 @@
</script>
<ConfirmDialog
id="delete-user-confirmation-modal"
title="Delete user"
confirmText={forceDelete ? 'Permanently Delete' : 'Delete'}
onConfirm={handleDeleteUser}

View File

@ -28,7 +28,6 @@
</script>
<ConfirmDialog
id="restore-user-modal"
title="Restore user"
confirmText="Continue"
confirmColor="green"

View File

@ -42,12 +42,7 @@
</script>
{#if isConfirmOpen}
<ConfirmDialog
id="disable-login-modal"
title="Disable login"
onCancel={() => (isConfirmOpen = false)}
onConfirm={() => handleSave(true)}
>
<ConfirmDialog title="Disable login" onCancel={() => (isConfirmOpen = false)} onConfirm={() => handleSave(true)}>
<svelte:fragment slot="prompt">
<div class="flex flex-col gap-4">
<p>Are you sure you want to disable all login methods? Login will be completely disabled.</p>
@ -82,13 +77,7 @@
>.
</p>
<SettingSwitch
id="login-with-oauth"
{disabled}
title="ENABLE"
subtitle="Login with OAuth"
bind:checked={config.oauth.enabled}
/>
<SettingSwitch {disabled} title="ENABLE" subtitle="Login with OAuth" bind:checked={config.oauth.enabled} />
{#if config.oauth.enabled}
<hr />
@ -177,7 +166,6 @@
/>
<SettingSwitch
id="auto-register-new-users"
title="AUTO REGISTER"
subtitle="Automatically register new users after signing in with OAuth"
bind:checked={config.oauth.autoRegister}
@ -185,7 +173,6 @@
/>
<SettingSwitch
id="auto-launch-oauth"
title="AUTO LAUNCH"
subtitle="Start the OAuth login flow automatically upon navigating to the login page"
disabled={disabled || !config.oauth.enabled}
@ -193,7 +180,6 @@
/>
<SettingSwitch
id="mobile-redirect-uri-override"
title="MOBILE REDIRECT URI OVERRIDE"
subtitle="Enable when 'app.immich:/' is an invalid redirect URI."
disabled={disabled || !config.oauth.enabled}
@ -219,7 +205,6 @@
<div class="ml-4 mt-4 flex flex-col gap-4">
<div class="ml-4 mt-4 flex flex-col">
<SettingSwitch
id="enable-password-login"
title="ENABLED"
{disabled}
subtitle="Login with email and password"

View File

@ -234,7 +234,6 @@
/>
<SettingSwitch
id="two-pass-encoding"
title="TWO-PASS ENCODING"
{disabled}
subtitle="Transcode in two passes to produce better encoded videos. When max bitrate is enabled (required for it to work with H.264 and HEVC), this mode uses a bitrate range based on the max bitrate and ignores CRF. For VP9, CRF can be used if max bitrate is disabled."
@ -277,7 +276,6 @@
/>
<SettingSwitch
id="hardware-decoding"
title="HARDWARE DECODING"
{disabled}
subtitle="Applies only to NVENC and RKMPP. Enables end-to-end acceleration instead of only accelerating encoding. May not work on all videos."
@ -299,7 +297,6 @@
/>
<SettingSwitch
id="temporal-aq"
title="TEMPORAL AQ"
{disabled}
subtitle="Applies only to NVENC. Increases quality of high-detail, low-motion scenes. May not be compatible with older devices."

View File

@ -93,7 +93,6 @@
/>
<SettingSwitch
id="prefer-wide-gamut"
title="PREFER WIDE GAMUT"
subtitle="Use Display P3 for thumbnails. This better preserves the vibrance of images with wide colorspaces, but images may appear differently on old devices with an old browser version. sRGB images are kept as sRGB to avoid color shifts."
checked={config.image.colorspace === Colorspace.P3}
@ -103,7 +102,6 @@
/>
<SettingSwitch
id="prefer-embedded"
title="PREFER EMBEDDED PREVIEW"
subtitle="Use embedded previews in RAW photos as the input to image processing when available. This can produce more accurate colors for some images, but the quality of the preview is camera-dependent and the image may have more compression artifacts."
checked={config.image.extractEmbedded}

View File

@ -37,7 +37,6 @@
<form autocomplete="off" on:submit|preventDefault>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="watch-filesystem"
title="Watch filesystem"
{disabled}
subtitle="Watch external libraries for file changes"
@ -65,7 +64,6 @@
<form autocomplete="off" on:submit|preventDefault>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="periodic-library-scan"
title="ENABLED"
{disabled}
subtitle="Enable periodic library scanning"

View File

@ -20,13 +20,7 @@
<div in:fade={{ duration: 500 }}>
<form autocomplete="off" on:submit|preventDefault>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-logging"
title="ENABLED"
{disabled}
subtitle="Logging"
bind:checked={config.logging.enabled}
/>
<SettingSwitch title="ENABLED" {disabled} subtitle="Logging" bind:checked={config.logging.enabled} />
<SettingSelect
label="LEVEL"
desc="When enabled, what log level to use."

View File

@ -26,7 +26,6 @@
<form autocomplete="off" on:submit|preventDefault class="mx-4 mt-4">
<div class="flex flex-col gap-4">
<SettingSwitch
id="enable-machine-learning"
title="ENABLED"
subtitle="If disabled, all ML features will be disabled regardless of the below settings."
{disabled}
@ -53,7 +52,6 @@
>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-clip"
title="ENABLED"
subtitle="If disabled, images will not be encoded for smart search."
bind:checked={config.machineLearning.clip.enabled}
@ -85,7 +83,6 @@
>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-duplicate-detection"
title="ENABLED"
subtitle="If disabled, exactly identical assets will still be de-duplicated."
bind:checked={config.machineLearning.duplicateDetection.enabled}
@ -116,7 +113,6 @@
>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-facial-recognition"
title="ENABLED"
subtitle="If disabled, images will not be encoded for facial recognition and will not populate the People section in the Explore page."
bind:checked={config.machineLearning.facialRecognition.enabled}

View File

@ -26,7 +26,6 @@
<SettingAccordion key="map" title="Map Settings" subtitle="Manage map settings">
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-map-features"
title="ENABLED"
{disabled}
subtitle="Enable map features"
@ -67,7 +66,6 @@
</svelte:fragment>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-reverse-geocoding"
title="ENABLED"
{disabled}
subtitle="Enable reverse geocoding"

View File

@ -20,7 +20,6 @@
<form autocomplete="off" on:submit|preventDefault>
<div class="ml-4 mt-4">
<SettingSwitch
id="enable-new-version-check"
title="ENABLED"
subtitle="Enable periodic requests to GitHub to check for new releases"
bind:checked={config.newVersionCheck.enabled}

View File

@ -26,7 +26,6 @@
<SettingAccordion key="email" title="Email" subtitle="Settings for sending email notifications">
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-smtp"
title="Enabled"
subtitle="Enable email notifications"
{disabled}
@ -76,7 +75,6 @@
/>
<SettingSwitch
id="enable-ignore-cert"
title="Ignore certificate errors"
subtitle="Ignore TLS certificate validation errors (not recommended)"
disabled={disabled || !config.notifications.smtp.enabled}

View File

@ -107,7 +107,6 @@
{#await getTemplateOptions() then}
<div id="directory-path-builder" class="flex flex-col gap-4 {minified ? '' : 'ml-4 mt-4'}">
<SettingSwitch
id="storage-template-enabled"
title="ENABLED"
{disabled}
subtitle="Enable storage template engine"
@ -117,7 +116,6 @@
{#if !minified}
<SettingSwitch
id="hash-verification-enabled"
title="HASH VERIFICATION ENABLED"
{disabled}
subtitle="Enables hash verification, don't disable this unless you're certain of the implications"

View File

@ -23,7 +23,6 @@
<form autocomplete="off" on:submit|preventDefault>
<div class="ml-4 mt-4 flex flex-col gap-4">
<SettingSwitch
id="enable-trash-features"
title="ENABLED"
{disabled}
subtitle="Enable Trash features"

View File

@ -50,7 +50,7 @@
};
</script>
<FullScreenModal id="album-options-modal" title="Options" onClose={() => dispatch('close')}>
<FullScreenModal title="Options" onClose={() => dispatch('close')}>
<div class="items-center justify-center">
<div class="py-2">
<h2 class="text-gray text-sm mb-2">SETTINGS</h2>
@ -64,7 +64,6 @@
/>
{/if}
<SettingSwitch
id="comments-likes"
title="Comments & likes"
subtitle="Let others respond"
checked={album.isActivityEnabled}

View File

@ -87,7 +87,7 @@
</script>
{#if !selectedRemoveUser}
<FullScreenModal id="share-info-modal" title="Options" {onClose}>
<FullScreenModal title="Options" {onClose}>
<section class="immich-scrollbar max-h-[400px] overflow-y-auto pb-4">
<div class="flex w-full place-items-center justify-between gap-4 p-5">
<div class="flex place-items-center gap-4">
@ -156,7 +156,6 @@
{#if selectedRemoveUser && selectedRemoveUser?.id === currentUser?.id}
<ConfirmDialog
id="leave-album-modal"
title="Leave album?"
prompt="Are you sure you want to leave {album.albumName}?"
confirmText="Leave"
@ -167,7 +166,6 @@
{#if selectedRemoveUser && selectedRemoveUser?.id !== currentUser?.id}
<ConfirmDialog
id="remove-user-modal"
title="Remove user?"
prompt="Are you sure you want to remove {selectedRemoveUser.name}?"
confirmText="Remove"

View File

@ -70,7 +70,7 @@
};
</script>
<FullScreenModal id="user-selection-modal" title="Invite to album" showLogo {onClose}>
<FullScreenModal title="Invite to album" showLogo {onClose}>
{#if Object.keys(selectedUsers).length > 0}
<div class="mb-2 py-2 sticky">
<p class="text-xs font-medium">SELECTED</p>

View File

@ -30,7 +30,7 @@
};
</script>
<FullScreenModal id="merge-people-modal" title="Merge people - {title}" onClose={() => dispatch('close')}>
<FullScreenModal title="Merge people - {title}" onClose={() => dispatch('close')}>
<div class="flex items-center justify-center py-4 md:h-36 md:py-4">
{#if !choosePersonToMerge}
<div class="flex h-20 w-20 items-center px-1 md:h-24 md:w-24 md:px-2">

View File

@ -20,7 +20,7 @@
};
</script>
<FullScreenModal id="set-birth-date-modal" title="Set date of birth" icon={mdiCake} onClose={handleCancel}>
<FullScreenModal title="Set date of birth" icon={mdiCake} onClose={handleCancel}>
<div class="text-immich-primary dark:text-immich-dark-primary">
<p class="text-sm dark:text-immich-dark-fg">
Date of birth is used to calculate the age of this person at the time of a photo.

View File

@ -28,7 +28,7 @@
};
</script>
<FullScreenModal id="api-key-modal" {title} icon={mdiKeyVariant} onClose={handleCancel}>
<FullScreenModal {title} icon={mdiKeyVariant} onClose={handleCancel}>
<form on:submit|preventDefault={handleSubmit} autocomplete="off" id="api-key-form">
<div class="mb-4 flex flex-col gap-2">
<label class="immich-form-label" for="name">Name</label>

View File

@ -13,7 +13,7 @@
const handleDone = () => dispatch('done');
</script>
<FullScreenModal id="api-key-secret-modal" title="API key" icon={mdiKeyVariant} onClose={() => handleDone()}>
<FullScreenModal title="API key" icon={mdiKeyVariant} onClose={() => handleDone()}>
<div class="text-immich-primary dark:text-immich-dark-primary">
<p class="text-sm dark:text-immich-dark-fg">
This value will only be shown once. Please be sure to copy it before closing the window.

View File

@ -74,7 +74,7 @@
}
</script>
<FullScreenModal id="create-new-user-modal" title="Create new user" showLogo {onClose}>
<FullScreenModal title="Create new user" showLogo {onClose}>
<form on:submit|preventDefault={registerUser} autocomplete="off" id="create-new-user-form">
<div class="my-4 flex flex-col gap-2">
<label class="immich-form-label" for="email">Email</label>

View File

@ -36,7 +36,7 @@
};
</script>
<FullScreenModal id="edit-album-modal" title="Edit album" width="wide" {onClose}>
<FullScreenModal title="Edit album" width="wide" {onClose}>
<form on:submit|preventDefault={handleUpdateAlbumInfo} autocomplete="off" id="edit-album-form">
<div class="flex items-center">
<div class="hidden sm:flex">

View File

@ -96,7 +96,7 @@
}
</script>
<FullScreenModal id="edit-user-modal" title="Edit user" icon={mdiAccountEditOutline} {onClose}>
<FullScreenModal title="Edit user" icon={mdiAccountEditOutline} {onClose}>
<form on:submit|preventDefault={editUser} autocomplete="off" id="edit-user-form">
<div class="my-4 flex flex-col gap-2">
<label class="immich-form-label" for="email">Email</label>

View File

@ -28,12 +28,7 @@
const handleSubmit = () => dispatch('submit', { excludePattern: exclusionPattern });
</script>
<FullScreenModal
id="add-exclusion-pattern-modal"
title="Add exclusion pattern"
icon={mdiFolderRemove}
onClose={handleCancel}
>
<FullScreenModal title="Add exclusion pattern" icon={mdiFolderRemove} onClose={handleCancel}>
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="add-exclusion-pattern-form">
<p class="py-5 text-sm">
Exclusion patterns lets you ignore files and folders when scanning your library. This is useful if you have

View File

@ -30,7 +30,7 @@
const handleSubmit = () => dispatch('submit', { importPath });
</script>
<FullScreenModal id="library-import-path-modal" {title} icon={mdiFolderSync} onClose={handleCancel}>
<FullScreenModal {title} icon={mdiFolderSync} onClose={handleCancel}>
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="library-import-path-form">
<p class="py-5 text-sm">
Specify a folder to import. This folder, including subfolders, will be scanned for images and videos.

View File

@ -27,12 +27,7 @@
const handleSubmit = () => dispatch('submit', { ownerId });
</script>
<FullScreenModal
id="select-library-owner-modal"
title="Select library owner"
icon={mdiFolderSync}
onClose={handleCancel}
>
<FullScreenModal title="Select library owner" icon={mdiFolderSync} onClose={handleCancel}>
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="select-library-owner-form">
<p class="p-5 text-sm">NOTE: This cannot be changed later!</p>

View File

@ -21,21 +21,17 @@
const handleClose = () => dispatch('close');
</script>
<FullScreenModal id="map-settings-modal" title="Map settings" onClose={handleClose}>
<FullScreenModal title="Map settings" onClose={handleClose}>
<form
on:submit|preventDefault={() => dispatch('save', settings)}
class="flex flex-col gap-4 text-immich-primary dark:text-immich-dark-primary"
id="map-settings-form"
>
<SettingSwitch id="allow-dark-mode" title="Allow dark mode" bind:checked={settings.allowDarkMode} />
<SettingSwitch id="only-favorites" title="Only favorites" bind:checked={settings.onlyFavorites} />
<SettingSwitch id="include-archived" title="Include archived" bind:checked={settings.includeArchived} />
<SettingSwitch
id="include-shared-partner-assets"
title="Include shared partner assets"
bind:checked={settings.withPartners}
/>
<SettingSwitch id="include-shared-albums" title="Include shared albums" bind:checked={settings.withSharedAlbums} />
<SettingSwitch title="Allow dark mode" bind:checked={settings.allowDarkMode} />
<SettingSwitch title="Only favorites" bind:checked={settings.onlyFavorites} />
<SettingSwitch title="Include archived" bind:checked={settings.includeArchived} />
<SettingSwitch title="Include shared partner assets" bind:checked={settings.withPartners} />
<SettingSwitch title="Include shared albums" bind:checked={settings.withSharedAlbums} />
{#if customDateRange}
<div in:fly={{ y: 10, duration: 200 }} class="flex flex-col gap-4">
<div class="flex items-center justify-between gap-8">

View File

@ -23,7 +23,6 @@
</script>
<ConfirmDialog
id="permanently-delete-asset-modal"
title="Permanently delete asset{s(size)}"
confirmText="Delete"
onConfirm={handleConfirm}

View File

@ -53,7 +53,7 @@
};
</script>
<FullScreenModal id="album-selection-modal" title={getTitle()} {onClose}>
<FullScreenModal title={getTitle()} {onClose}>
<div class="mb-2 flex max-h-[400px] flex-col">
{#if loading}
{#each { length: 3 } as _}

View File

@ -56,7 +56,6 @@
</script>
<ConfirmDialog
id="edit-date-time-modal"
confirmColor="primary"
title="Edit date and time"
prompt="Please select a new date:"
@ -75,13 +74,7 @@
/>
</div>
<div class="flex flex-col w-full mt-2">
<Combobox
bind:selectedOption
id="settings-timezone"
label="Timezone"
options={timezones}
placeholder="Search timezone..."
/>
<Combobox bind:selectedOption label="Timezone" options={timezones} placeholder="Search timezone..." />
</div>
</div>
</ConfirmDialog>

View File

@ -104,7 +104,6 @@
</script>
<ConfirmDialog
id="change-location-modal"
confirmColor="primary"
title="Change location"
width="wide"

View File

@ -19,17 +19,18 @@
import { clickOutside } from '$lib/actions/click-outside';
import { focusOutside } from '$lib/actions/focus-outside';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { uniqueIdStore } from '$lib/stores/unique-id.store';
/**
* Unique identifier for the combobox.
*/
export let id: string;
export let label: string;
export let hideLabel = false;
export let options: ComboBoxOption[] = [];
export let selectedOption: ComboBoxOption | undefined;
export let placeholder = '';
/**
* Unique identifier for the combobox.
*/
let id: string = uniqueIdStore.generateId();
/**
* Indicates whether or not the dropdown autocomplete list should be visible.
*/

View File

@ -159,7 +159,7 @@
};
</script>
<FullScreenModal id="create-shared-link-modal" title={getTitle()} icon={mdiLink} {onClose}>
<FullScreenModal title={getTitle()} icon={mdiLink} {onClose}>
<section>
{#if shareType === SharedLinkType.Album}
{#if !editingLink}
@ -204,33 +204,25 @@
</div>
<div class="my-3">
<SettingSwitch id="require-password" bind:checked={enablePassword} title={'Require password'} />
<SettingSwitch bind:checked={enablePassword} title={'Require password'} />
</div>
<div class="my-3">
<SettingSwitch id="show-metadata" bind:checked={showMetadata} title={'Show metadata'} />
<SettingSwitch bind:checked={showMetadata} title={'Show metadata'} />
</div>
<div class="my-3">
<SettingSwitch
id="allow-public-download"
bind:checked={allowDownload}
title={'Allow public user to download'}
/>
<SettingSwitch bind:checked={allowDownload} title={'Allow public user to download'} />
</div>
<div class="my-3">
<SettingSwitch id="allow-public-upload" bind:checked={allowUpload} title={'Allow public user to upload'} />
<SettingSwitch bind:checked={allowUpload} title={'Allow public user to upload'} />
</div>
<div class="text-sm">
{#if editingLink}
<p class="immich-form-label my-2">
<SettingSwitch
id="change-expiration-time"
bind:checked={shouldChangeExpirationTime}
title={'Change expiration time'}
/>
<SettingSwitch bind:checked={shouldChangeExpirationTime} title={'Change expiration time'} />
</p>
{:else}
<p class="immich-form-label my-2">Expire after</p>

View File

@ -3,7 +3,6 @@
import Button from '../../elements/buttons/button.svelte';
import type { Color } from '$lib/components/elements/buttons/button.svelte';
export let id: string = 'confirm-dialog';
export let title = 'Confirm';
export let prompt = 'Are you sure you want to do this?';
export let confirmText = 'Confirm';
@ -24,7 +23,7 @@
};
</script>
<FullScreenModal {title} {id} onClose={onCancel} {width}>
<FullScreenModal {title} onClose={onCancel} {width}>
<div class="text-md py-5 text-center">
<slot name="prompt">
<p>{prompt}</p>

View File

@ -3,13 +3,9 @@
import { fade } from 'svelte/transition';
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
import ModalHeader from '$lib/components/shared-components/modal-header.svelte';
import { uniqueIdStore } from '$lib/stores/unique-id.store';
export let onClose: () => void;
/**
* Unique identifier for the modal.
*/
export let id: string;
export let title: string;
/**
* If true, the logo will be displayed next to the modal title.
@ -28,6 +24,11 @@
*/
export let width: 'wide' | 'narrow' | 'auto' = 'narrow';
/**
* Unique identifier for the modal.
*/
let id: string = uniqueIdStore.generateId();
$: titleId = `${id}-title`;
$: isStickyBottom = !!$$slots['sticky-bottom'];

View File

@ -13,7 +13,7 @@
const colors: UserAvatarColor[] = Object.values(UserAvatarColor);
</script>
<FullScreenModal id="avatar-selector-modal" title="Select avatar color" width="auto" onClose={() => dispatch('close')}>
<FullScreenModal title="Select avatar color" width="auto" onClose={() => dispatch('close')}>
<div class="flex items-center justify-center mt-4">
<div class="grid grid-cols-2 md:grid-cols-5 gap-4">
{#each colors as color}

View File

@ -69,7 +69,7 @@
};
</script>
<FullScreenModal id="profile-image-cropper" title="Set profile picture" width="auto" {onClose}>
<FullScreenModal title="Set profile picture" width="auto" {onClose}>
<div class="flex place-items-center items-center justify-center">
<div
class="relative flex aspect-square w-[250px] overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"

View File

@ -41,7 +41,6 @@
<div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
<div class="w-full">
<Combobox
id="camera-make"
label="Make"
on:select={({ detail }) => (filters.make = detail?.value)}
options={toComboBoxOptions(makes)}
@ -52,7 +51,6 @@
<div class="w-full">
<Combobox
id="camera-model"
label="Model"
on:select={({ detail }) => (filters.model = detail?.value)}
options={toComboBoxOptions(models)}

View File

@ -63,7 +63,6 @@
<div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
<div class="w-full">
<Combobox
id="location-country"
label="Country"
on:select={({ detail }) => (filters.country = detail?.value)}
options={toComboBoxOptions(countries)}
@ -74,7 +73,6 @@
<div class="w-full">
<Combobox
id="location-state"
label="State"
on:select={({ detail }) => (filters.state = detail?.value)}
options={toComboBoxOptions(states)}
@ -85,7 +83,6 @@
<div class="w-full">
<Combobox
id="location-city"
label="City"
on:select={({ detail }) => (filters.city = detail?.value)}
options={toComboBoxOptions(cities)}

View File

@ -3,7 +3,6 @@
import { fly } from 'svelte/transition';
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
export let id: string;
export let title: string;
export let comboboxPlaceholder: string;
export let subtitle = '';
@ -33,7 +32,6 @@
</div>
<div class="flex items-center">
<Combobox
{id}
label={title}
hideLabel={true}
{selectedOption}

View File

@ -3,14 +3,16 @@
import { fly } from 'svelte/transition';
import { createEventDispatcher } from 'svelte';
import Slider from '$lib/components/elements/slider.svelte';
import { uniqueIdStore } from '$lib/stores/unique-id.store';
export let id: string;
export let title: string;
export let subtitle = '';
export let checked = false;
export let disabled = false;
export let isEdited = false;
let id: string = uniqueIdStore.generateId();
$: sliderId = `${id}-slider`;
$: subtitleId = subtitle ? `${id}-subtitle` : undefined;

View File

@ -37,12 +37,7 @@
}>();
</script>
<FullScreenModal
id="keyboard-shortcuts-modal"
title="Keyboard shortcuts"
width="auto"
onClose={() => dispatch('close')}
>
<FullScreenModal title="Keyboard shortcuts" width="auto" onClose={() => dispatch('close')}>
<div class="grid grid-cols-1 gap-4 px-4 pb-4 md:grid-cols-2">
<div class="p-4">
<h2>General</h2>

View File

@ -33,7 +33,7 @@
</script>
{#if showModal}
<FullScreenModal id="new-version-modal" title="🎉 NEW VERSION AVAILABLE" onClose={() => (showModal = false)}>
<FullScreenModal title="🎉 NEW VERSION AVAILABLE" onClose={() => (showModal = false)}>
<div>
Hi friend, there is a new version of the application please take your time to visit the
<span class="font-medium underline"

View File

@ -45,7 +45,7 @@
};
</script>
<FullScreenModal id="slideshow-settings-modal" title="Slideshow settings" {onClose}>
<FullScreenModal title="Slideshow settings" {onClose}>
<div class="flex flex-col gap-4 text-immich-primary dark:text-immich-dark-primary">
<SettingDropdown
title="Direction"
@ -63,7 +63,7 @@
$slideshowLook = handleToggle(option, lookOptions) || $slideshowLook;
}}
/>
<SettingSwitch id="show-progress-bar" title="Show Progress Bar" bind:checked={$showProgressBar} />
<SettingSwitch title="Show Progress Bar" bind:checked={$showProgressBar} />
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label="Duration"

View File

@ -72,7 +72,6 @@
<div class="ml-4 mt-4 flex flex-col gap-4">
<div class="ml-4">
<SettingSwitch
id="theme-selection"
title="Theme selection"
subtitle="Automatically set the theme to light or dark based on your browser's system preference"
bind:checked={$colorTheme.system}
@ -82,7 +81,6 @@
<div class="ml-4">
<SettingSwitch
id="default-locale"
title="Default Locale"
subtitle="Format dates and numbers based on your browser locale"
checked={$locale == undefined}
@ -94,7 +92,6 @@
{#if $locale !== undefined}
<div class="ml-4">
<SettingCombobox
id="custom-locale"
comboboxPlaceholder="Searching locales..."
{selectedOption}
options={getAllLanguages()}
@ -107,7 +104,6 @@
<div class="ml-4">
<SettingSwitch
id="always-load-original-file"
title="Display original photos"
subtitle="Prefer to display the original photo when viewing an asset rather than thumbnails when the original asset is web-compatible. This may result in slower photo display speeds."
bind:checked={$alwaysLoadOriginalFile}
@ -116,7 +112,6 @@
</div>
<div class="ml-4">
<SettingSwitch
id="play-video-thumbnail-on-hover"
title="Play video thumbnail on hover"
subtitle="Play video thumbnail when mouse is hovering over item. Even when disabled, playback can be started by hovering over the play icon."
bind:checked={$playVideoThumbnailOnHover}
@ -125,7 +120,6 @@
</div>
<div class="ml-4">
<SettingSwitch
id="loop-video"
title="Loop videos"
subtitle="Enable to automatically loop a video in the detail viewer."
bind:checked={$loopVideo}
@ -135,7 +129,6 @@
<div class="ml-4">
<SettingSwitch
id="show-delete-warning"
title="Permanent deletion warning"
subtitle="Show a warning when permanently deleting assets"
bind:checked={$showDeleteModal}
@ -144,7 +137,6 @@
<div class="ml-4">
<SettingSwitch
id="people-sidebar-link"
title="People"
subtitle="Display a link to People in the sidebar"
bind:checked={$sidebarSettings.people}
@ -152,7 +144,6 @@
</div>
<div class="ml-4">
<SettingSwitch
id="sharing-sidebar-link"
title="Sharing"
subtitle="Display a link to Sharing in the sidebar"
bind:checked={$sidebarSettings.sharing}

View File

@ -31,7 +31,6 @@
<div class="ml-4 mt-4 flex flex-col gap-4">
<div class="ml-4">
<SettingSwitch
id="time-based-memories"
title="Time-based memories"
subtitle="Photos from previous years"
bind:checked={memoriesEnabled}

View File

@ -32,7 +32,7 @@
};
</script>
<FullScreenModal id="partner-selection-modal" title="Add partner" showLogo {onClose}>
<FullScreenModal title="Add partner" showLogo {onClose}>
<div class="immich-scrollbar max-h-[300px] overflow-y-auto">
{#if availableUsers.length > 0}
{#each availableUsers as user}

View File

@ -167,7 +167,6 @@
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
<p class="text-xs font-medium my-4">PHOTOS FROM {partner.user.name.toUpperCase()}</p>
<SettingSwitch
id="show-in-timeline"
title="Show in timeline"
subtitle="Show photos and videos from this user in your timeline"
bind:checked={partner.inTimeline}

View File

@ -0,0 +1,14 @@
import { uniqueIdStore } from '$lib/stores/unique-id.store';
describe('uniqueIdStore', () => {
afterEach(() => {
uniqueIdStore.update(() => -1);
});
it('should generate unique ids', () => {
const { generateId } = uniqueIdStore;
const ids = [generateId(), generateId(), generateId()];
expect(ids).toEqual(['id-0', 'id-1', 'id-2']);
});
});

View File

@ -0,0 +1,16 @@
import { get, writable } from 'svelte/store';
function createIdStore() {
const { subscribe, update } = writable(-1);
return {
subscribe,
update,
generateId: () => {
update((value) => value + 1);
return `id-${get(uniqueIdStore)}`;
},
};
}
export const uniqueIdStore = createIdStore();

View File

@ -453,7 +453,7 @@
{/if}
{#if showChangeNameModal}
<FullScreenModal id="change-name-modal" title="Change name" onClose={() => (showChangeNameModal = false)}>
<FullScreenModal title="Change name" onClose={() => (showChangeNameModal = false)}>
<form on:submit|preventDefault={submitNameChange} autocomplete="off" id="change-name-form">
<div class="flex flex-col gap-2">
<label class="immich-form-label" for="name">Name</label>

View File

@ -154,7 +154,6 @@
{#if shouldShowPasswordResetSuccess}
<ConfirmDialog
id="password-reset-success-modal"
title="Password reset success"
confirmText="Done"
onConfirm={() => (shouldShowPasswordResetSuccess = false)}