mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
refactor: modals (#25162)
This commit is contained in:
parent
da248414af
commit
702499b97d
11 changed files with 144 additions and 189 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import PinCodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte';
|
import PinCodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte';
|
||||||
|
import PinCodeResetModal from '$lib/modals/PinCodeResetModal.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { changePinCode } from '@immich/sdk';
|
import { changePinCode } from '@immich/sdk';
|
||||||
import { Button, Heading, Text, toastManager } from '@immich/ui';
|
import { Button, Heading, modalManager, Text, toastManager } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
|
|
@ -12,12 +13,6 @@
|
||||||
let isLoading = $state(false);
|
let isLoading = $state(false);
|
||||||
let canSubmit = $derived(currentPinCode.length === 6 && confirmPinCode.length === 6 && newPinCode === confirmPinCode);
|
let canSubmit = $derived(currentPinCode.length === 6 && confirmPinCode.length === 6 && newPinCode === confirmPinCode);
|
||||||
|
|
||||||
type Props = {
|
|
||||||
onForgot: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
let { onForgot }: Props = $props();
|
|
||||||
|
|
||||||
const handleSubmit = async (event: Event) => {
|
const handleSubmit = async (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
await handleChangePinCode();
|
await handleChangePinCode();
|
||||||
|
|
@ -51,7 +46,7 @@
|
||||||
<PinCodeInput label={$t('current_pin_code')} bind:value={currentPinCode} tabindexStart={1} pinLength={6} />
|
<PinCodeInput label={$t('current_pin_code')} bind:value={currentPinCode} tabindexStart={1} pinLength={6} />
|
||||||
<PinCodeInput label={$t('new_pin_code')} bind:value={newPinCode} tabindexStart={7} pinLength={6} />
|
<PinCodeInput label={$t('new_pin_code')} bind:value={newPinCode} tabindexStart={7} pinLength={6} />
|
||||||
<PinCodeInput label={$t('confirm_new_pin_code')} bind:value={confirmPinCode} tabindexStart={13} pinLength={6} />
|
<PinCodeInput label={$t('confirm_new_pin_code')} bind:value={confirmPinCode} tabindexStart={13} pinLength={6} />
|
||||||
<button type="button" onclick={onForgot}>
|
<button type="button" onclick={() => modalManager.show(PinCodeResetModal, {})}>
|
||||||
<Text color="muted" class="underline" size="small">{$t('forgot_pin_code_question')}</Text>
|
<Text color="muted" class="underline" size="small">{$t('forgot_pin_code_question')}</Text>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||||
import PinCodeChangeForm from '$lib/components/user-settings-page/PinCodeChangeForm.svelte';
|
import PinCodeChangeForm from '$lib/components/user-settings-page/PinCodeChangeForm.svelte';
|
||||||
import PinCodeCreateForm from '$lib/components/user-settings-page/PinCodeCreateForm.svelte';
|
import PinCodeCreateForm from '$lib/components/user-settings-page/PinCodeCreateForm.svelte';
|
||||||
import PinCodeResetModal from '$lib/modals/PinCodeResetModal.svelte';
|
|
||||||
import { getAuthStatus } from '@immich/sdk';
|
import { getAuthStatus } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
|
|
@ -14,18 +13,17 @@
|
||||||
hasPinCode = pinCode;
|
hasPinCode = pinCode;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleResetPINCode = async () => {
|
const onUserPinCodeReset = () => {
|
||||||
const success = await modalManager.show(PinCodeResetModal, {});
|
|
||||||
if (success) {
|
|
||||||
hasPinCode = false;
|
hasPinCode = false;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<OnEvents {onUserPinCodeReset} />
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
{#if hasPinCode}
|
{#if hasPinCode}
|
||||||
<div in:fade={{ duration: 200 }}>
|
<div in:fade={{ duration: 200 }}>
|
||||||
<PinCodeChangeForm onForgot={handleResetPINCode} />
|
<PinCodeChangeForm />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div in:fade={{ duration: 200 }}>
|
<div in:fade={{ duration: 200 }}>
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ export type Events = {
|
||||||
TagUpdate: [TagResponseDto];
|
TagUpdate: [TagResponseDto];
|
||||||
TagDelete: [TreeNode];
|
TagDelete: [TreeNode];
|
||||||
|
|
||||||
|
UserPinCodeReset: [];
|
||||||
|
|
||||||
UserAdminCreate: [UserAdminResponseDto];
|
UserAdminCreate: [UserAdminResponseDto];
|
||||||
UserAdminUpdate: [UserAdminResponseDto];
|
UserAdminUpdate: [UserAdminResponseDto];
|
||||||
UserAdminRestore: [UserAdminResponseDto];
|
UserAdminRestore: [UserAdminResponseDto];
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ApiKeyPermissionsPicker from '$lib/components/ApiKeyPermissionsPicker.svelte';
|
import ApiKeyPermissionsPicker from '$lib/components/ApiKeyPermissionsPicker.svelte';
|
||||||
|
import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte';
|
||||||
import { handleCreateApiKey } from '$lib/services/api-key.service';
|
import { handleCreateApiKey } from '$lib/services/api-key.service';
|
||||||
import { Permission } from '@immich/sdk';
|
import { Permission } from '@immich/sdk';
|
||||||
import { Field, FormModal, Input } from '@immich/ui';
|
import { Field, FormModal, Input, modalManager } from '@immich/ui';
|
||||||
import { mdiKeyVariant } from '@mdi/js';
|
import { mdiKeyVariant } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -15,11 +16,11 @@
|
||||||
const isAllPermissions = $derived(selectedPermissions.length === Object.keys(Permission).length - 1);
|
const isAllPermissions = $derived(selectedPermissions.length === Object.keys(Permission).length - 1);
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
const success = await handleCreateApiKey({
|
const permissions = isAllPermissions ? [Permission.All] : selectedPermissions;
|
||||||
name,
|
const response = await handleCreateApiKey({ name, permissions });
|
||||||
permissions: isAllPermissions ? [Permission.All] : selectedPermissions,
|
if (response) {
|
||||||
});
|
// no nested modal
|
||||||
if (success) {
|
void modalManager.show(ApiKeySecretModal, { secret: response.secret });
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleCreateJob } from '$lib/services/job.service';
|
||||||
import { createJob, ManualJobName } from '@immich/sdk';
|
import { ManualJobName } from '@immich/sdk';
|
||||||
import { ConfirmModal, toastManager } from '@immich/ui';
|
import { FormModal } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
type Props = { onClose: (confirmed: boolean) => void };
|
type Props = { onClose: () => void };
|
||||||
|
|
||||||
let { onClose }: Props = $props();
|
let { onClose }: Props = $props();
|
||||||
|
|
||||||
|
|
@ -20,42 +20,18 @@
|
||||||
|
|
||||||
let selectedJob: ComboBoxOption | undefined = $state(undefined);
|
let selectedJob: ComboBoxOption | undefined = $state(undefined);
|
||||||
|
|
||||||
const onsubmit = async (event: Event) => {
|
const onSubmit = async () => {
|
||||||
event.preventDefault();
|
|
||||||
await handleCreate();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreate = async () => {
|
|
||||||
if (!selectedJob) {
|
if (!selectedJob) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const success = await handleCreateJob({ name: selectedJob.value as ManualJobName });
|
||||||
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
|
if (success) {
|
||||||
toastManager.success($t('admin.job_created'));
|
onClose();
|
||||||
onClose(true);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_submit_job'));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ConfirmModal
|
<FormModal title={$t('admin.create_job')} submitText={$t('create')} disabled={!selectedJob} {onClose} {onSubmit}>
|
||||||
confirmColor="primary"
|
<Combobox bind:selectedOption={selectedJob} label={$t('jobs')} {options} placeholder={$t('admin.search_jobs')} />
|
||||||
title={$t('admin.create_job')}
|
</FormModal>
|
||||||
disabled={!selectedJob}
|
|
||||||
onClose={(confirmed) => (confirmed ? handleCreate() : onClose(false))}
|
|
||||||
>
|
|
||||||
{#snippet promptSnippet()}
|
|
||||||
<form {onsubmit} autocomplete="off" id="create-tag-form" class="w-full">
|
|
||||||
<div class="flex flex-col gap-1 text-start">
|
|
||||||
<Combobox
|
|
||||||
bind:selectedOption={selectedJob}
|
|
||||||
label={$t('jobs')}
|
|
||||||
{options}
|
|
||||||
placeholder={$t('admin.search_jobs')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{/snippet}
|
|
||||||
</ConfirmModal>
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
|
||||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||||
import { SettingInputFieldType } from '$lib/constants';
|
import { handleCreateApiKey } from '$lib/services/api-key.service';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { Permission } from '@immich/sdk';
|
||||||
import { createApiKey, Permission } from '@immich/sdk';
|
import { Button, Field, Input, Modal, ModalBody, obtainiumBadge, Text } from '@immich/ui';
|
||||||
import { Button, Modal, ModalBody, obtainiumBadge, Text } from '@immich/ui';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
let inputUrl = $state(location.origin);
|
let inputUrl = $state(location.origin);
|
||||||
let inputApiKey = $state('');
|
let inputApiKey = $state('');
|
||||||
|
|
@ -12,42 +10,35 @@
|
||||||
let obtainiumLink = $derived(
|
let obtainiumLink = $derived(
|
||||||
`https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22app.alextran.immich%22%2C%22url%22%3A%22${inputUrl}%2Fapi%2Fserver%2Fapk-links%22%2C%22author%22%3A%22Immich%22%2C%22name%22%3A%22Immich%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22intermediateLink%5C%22%3A%5B%5D%2C%5C%22customLinkFilterRegex%5C%22%3A%5C%22%5C%22%2C%5C%22filterByLinkText%5C%22%3Afalse%2C%5C%22skipSort%5C%22%3Afalse%2C%5C%22reverseSort%5C%22%3Afalse%2C%5C%22sortByLastLinkSegment%5C%22%3Afalse%2C%5C%22versionExtractWholePage%5C%22%3Afalse%2C%5C%22requestHeader%5C%22%3A%5B%7B%5C%22requestHeader%5C%22%3A%5C%22User-Agent%3A%20Mozilla%2F5.0%20(Linux%3B%20Android%2010%3B%20K)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F114.0.0.0%20Mobile%20Safari%2F537.36%5C%22%7D%2C%7B%5C%22requestHeader%5C%22%3A%5C%22x-api-key%3A%20${inputApiKey}%5C%22%7D%5D%2C%5C%22defaultPseudoVersioningMethod%5C%22%3A%5C%22APKLinkHash%5C%22%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%2Fv(%5C%5C%5C%5Cd%2B).(%5C%5C%5C%5Cd%2B).(%5C%5C%5C%5Cd%2B)%2F%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%241.%242.%243%5C%22%2C%5C%22versionDetection%5C%22%3Atrue%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22app-${archVariant}.apk%24%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3Anull%7D`,
|
`https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22app.alextran.immich%22%2C%22url%22%3A%22${inputUrl}%2Fapi%2Fserver%2Fapk-links%22%2C%22author%22%3A%22Immich%22%2C%22name%22%3A%22Immich%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22intermediateLink%5C%22%3A%5B%5D%2C%5C%22customLinkFilterRegex%5C%22%3A%5C%22%5C%22%2C%5C%22filterByLinkText%5C%22%3Afalse%2C%5C%22skipSort%5C%22%3Afalse%2C%5C%22reverseSort%5C%22%3Afalse%2C%5C%22sortByLastLinkSegment%5C%22%3Afalse%2C%5C%22versionExtractWholePage%5C%22%3Afalse%2C%5C%22requestHeader%5C%22%3A%5B%7B%5C%22requestHeader%5C%22%3A%5C%22User-Agent%3A%20Mozilla%2F5.0%20(Linux%3B%20Android%2010%3B%20K)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F114.0.0.0%20Mobile%20Safari%2F537.36%5C%22%7D%2C%7B%5C%22requestHeader%5C%22%3A%5C%22x-api-key%3A%20${inputApiKey}%5C%22%7D%5D%2C%5C%22defaultPseudoVersioningMethod%5C%22%3A%5C%22APKLinkHash%5C%22%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%2Fv(%5C%5C%5C%5Cd%2B).(%5C%5C%5C%5Cd%2B).(%5C%5C%5C%5Cd%2B)%2F%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%241.%242.%243%5C%22%2C%5C%22versionDetection%5C%22%3Atrue%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22app-${archVariant}.apk%24%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3Anull%7D`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
let { onClose }: Props = $props();
|
||||||
|
|
||||||
const handleCreate = async () => {
|
const handleCreate = async () => {
|
||||||
try {
|
const response = await handleCreateApiKey({ name: 'Obtainium', permissions: [Permission.ServerApkLinks] });
|
||||||
const { secret } = await createApiKey({
|
if (response) {
|
||||||
apiKeyCreateDto: {
|
inputApiKey = response.secret;
|
||||||
name: 'Obtainium',
|
|
||||||
permissions: [Permission.ServerApkLinks],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
inputApiKey = secret;
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_create_api_key'));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
interface Props {
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
let { onClose }: Props = $props();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('obtainium_configurator')} size="medium" {onClose}>
|
<Modal title={$t('obtainium_configurator')} size="medium" {onClose}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<Text color="muted" size="small">{$t('obtainium_configurator_instructions')}</Text>
|
||||||
<Text color="muted" size="small">
|
|
||||||
{$t('obtainium_configurator_instructions')}
|
|
||||||
</Text>
|
|
||||||
<form class="mt-4">
|
|
||||||
<div class="mt-2">
|
|
||||||
<SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('url')} bind:value={inputUrl} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-2 flex gap-2 place-items-center place-content-center">
|
<Field label={$t('url')} class="mt-4">
|
||||||
<SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('api_key')} bind:value={inputApiKey} />
|
<Input bind:value={inputUrl} />
|
||||||
|
</Field>
|
||||||
|
|
||||||
<div class="translate-y-[3px]">
|
<Field label={$t('api_key')} class="mt-4">
|
||||||
<Button size="small" onclick={() => handleCreate()}>{$t('create_api_key')}</Button>
|
<Input bind:value={inputApiKey} />
|
||||||
</div>
|
</Field>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-2">
|
||||||
|
<Button size="small" onclick={handleCreate}>{$t('create_api_key')}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SettingSelect
|
<SettingSelect
|
||||||
|
|
@ -60,7 +51,6 @@
|
||||||
{ value: 'x86_64-release', text: 'x86_64' },
|
{ value: 'x86_64-release', text: 'x86_64' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</form>
|
|
||||||
|
|
||||||
{#if inputUrl && inputApiKey && archVariant}
|
{#if inputUrl && inputApiKey && archVariant}
|
||||||
<div class="content-center">
|
<div class="content-center">
|
||||||
|
|
@ -73,11 +63,10 @@
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
id="obtainium-link"
|
id="obtainium-link"
|
||||||
>
|
>
|
||||||
<img class="pt-2 pr-5 h-20" alt="Get it on Obtainium" src={obtainiumBadge} />
|
<img class="pt-2 h-20" alt="Get it on Obtainium" src={obtainiumBadge} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleResetPinCode } from '$lib/services/user.service';
|
||||||
import { resetPinCode } from '@immich/sdk';
|
import { Field, FormModal, HelperText, Modal, ModalBody, PasswordInput, Stack, type ModalSize } from '@immich/ui';
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Field,
|
|
||||||
HelperText,
|
|
||||||
HStack,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalFooter,
|
|
||||||
PasswordInput,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
toastManager,
|
|
||||||
} from '@immich/ui';
|
|
||||||
import { mdiLockReset } from '@mdi/js';
|
import { mdiLockReset } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -24,55 +11,35 @@
|
||||||
|
|
||||||
let { onClose }: Props = $props();
|
let { onClose }: Props = $props();
|
||||||
|
|
||||||
let passwordLoginEnabled = $derived(featureFlagsManager.value.passwordLogin);
|
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
|
|
||||||
const handleReset = async () => {
|
const onSubmit = async () => {
|
||||||
try {
|
const success = await handleResetPinCode({ password });
|
||||||
await resetPinCode({ pinCodeResetDto: { password } });
|
if (success) {
|
||||||
toastManager.success($t('pin_code_reset_successfully'));
|
onClose();
|
||||||
onClose(true);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.failed_to_reset_pin_code'));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onsubmit = async (event: Event) => {
|
const common = $derived({ title: $t('reset'), size: 'small' as ModalSize, icon: mdiLockReset, onClose });
|
||||||
event.preventDefault();
|
|
||||||
await handleReset();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('reset_pin_code')} icon={mdiLockReset} size="small" {onClose}>
|
{#if featureFlagsManager.value.passwordLogin === false}
|
||||||
<ModalBody>
|
<FormModal {...common} submitColor="danger" submitText={$t('reset')} disabled={!password} {onSubmit}>
|
||||||
<form {onsubmit} autocomplete="off" id="reset-pin-form">
|
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<div>{$t('reset_pin_code_description')}</div>
|
<div>{$t('reset_pin_code_description')}</div>
|
||||||
{#if passwordLoginEnabled}
|
|
||||||
<hr class="my-2 h-px w-full border-0 bg-gray-200 dark:bg-gray-600" />
|
<hr class="my-2 h-px w-full border-0 bg-gray-200 dark:bg-gray-600" />
|
||||||
<section>
|
<section>
|
||||||
<Field label={$t('confirm_password')} required>
|
<Field label={$t('confirm_password')} required>
|
||||||
<PasswordInput bind:value={password} autocomplete="current-password" />
|
<PasswordInput bind:value={password} autocomplete="current-password" />
|
||||||
<HelperText>
|
<HelperText color="muted">{$t('reset_pin_code_with_password')}</HelperText>
|
||||||
<Text color="muted">{$t('reset_pin_code_with_password')}</Text>
|
|
||||||
</HelperText>
|
|
||||||
</Field>
|
</Field>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</form>
|
</FormModal>
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
{#if passwordLoginEnabled}
|
|
||||||
<HStack fullWidth>
|
|
||||||
<Button fullWidth shape="round" color="secondary" onclick={() => onClose()}>{$t('cancel')}</Button>
|
|
||||||
<Button type="submit" form="reset-pin-form" fullWidth shape="round" color="danger" disabled={!password}>
|
|
||||||
{$t('reset')}
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
{:else}
|
{:else}
|
||||||
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('close')}</Button>
|
<Modal {...common} closeOnBackdropClick>
|
||||||
{/if}
|
<ModalBody>
|
||||||
</ModalFooter>
|
<div>{$t('reset_pin_code_description')}</div>
|
||||||
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import ApiKeyCreateModal from '$lib/modals/ApiKeyCreateModal.svelte';
|
import ApiKeyCreateModal from '$lib/modals/ApiKeyCreateModal.svelte';
|
||||||
import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte';
|
|
||||||
import ApiKeyUpdateModal from '$lib/modals/ApiKeyUpdateModal.svelte';
|
import ApiKeyUpdateModal from '$lib/modals/ApiKeyUpdateModal.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
|
|
@ -56,14 +55,10 @@ export const handleCreateApiKey = async (dto: ApiKeyCreateDto) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { apiKey, secret } = await createApiKey({ apiKeyCreateDto: dto });
|
const response = await createApiKey({ apiKeyCreateDto: dto });
|
||||||
|
eventManager.emit('ApiKeyCreate', response.apiKey);
|
||||||
|
|
||||||
eventManager.emit('ApiKeyCreate', apiKey);
|
return response;
|
||||||
|
|
||||||
// no nested modal
|
|
||||||
void modalManager.show(ApiKeySecretModal, { secret });
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_create_api_key'));
|
handleError(error, $t('errors.unable_to_create_api_key'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
web/src/lib/services/job.service.ts
Normal file
16
web/src/lib/services/job.service.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
|
import { createJob, type JobCreateDto } from '@immich/sdk';
|
||||||
|
import { toastManager } from '@immich/ui';
|
||||||
|
|
||||||
|
export const handleCreateJob = async (dto: JobCreateDto) => {
|
||||||
|
const $t = await getFormatter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createJob({ jobCreateDto: dto });
|
||||||
|
toastManager.success($t('admin.job_created'));
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, $t('errors.unable_to_submit_job'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -64,9 +64,7 @@ export const getQueuesActions = ($t: MessageFormatter, queues: QueueResponseDto[
|
||||||
title: $t('admin.create_job'),
|
title: $t('admin.create_job'),
|
||||||
type: $t('command'),
|
type: $t('command'),
|
||||||
shortcuts: { shift: true, key: 'n' },
|
shortcuts: { shift: true, key: 'n' },
|
||||||
onAction: async () => {
|
onAction: () => modalManager.show(JobCreateModal, {}),
|
||||||
await modalManager.show(JobCreateModal, {});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManageConcurrency: ActionItem = {
|
const ManageConcurrency: ActionItem = {
|
||||||
|
|
|
||||||
18
web/src/lib/services/user.service.ts
Normal file
18
web/src/lib/services/user.service.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
|
import { resetPinCode, type PinCodeResetDto } from '@immich/sdk';
|
||||||
|
import { toastManager } from '@immich/ui';
|
||||||
|
|
||||||
|
export const handleResetPinCode = async (dto: PinCodeResetDto) => {
|
||||||
|
const $t = await getFormatter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await resetPinCode({ pinCodeResetDto: dto });
|
||||||
|
toastManager.success($t('pin_code_reset_successfully'));
|
||||||
|
eventManager.emit('UserPinCodeReset');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, $t('errors.failed_to_reset_pin_code'));
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue