mirror of
https://github.com/immich-app/immich.git
synced 2026-01-18 07:45:38 -08:00
refactor: modals (#25162)
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
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 { 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 { fade } from 'svelte/transition';
|
||||
|
||||
@@ -12,12 +13,6 @@
|
||||
let isLoading = $state(false);
|
||||
let canSubmit = $derived(currentPinCode.length === 6 && confirmPinCode.length === 6 && newPinCode === confirmPinCode);
|
||||
|
||||
type Props = {
|
||||
onForgot: () => void;
|
||||
};
|
||||
|
||||
let { onForgot }: Props = $props();
|
||||
|
||||
const handleSubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleChangePinCode();
|
||||
@@ -51,7 +46,7 @@
|
||||
<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('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>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||
import PinCodeChangeForm from '$lib/components/user-settings-page/PinCodeChangeForm.svelte';
|
||||
import PinCodeCreateForm from '$lib/components/user-settings-page/PinCodeCreateForm.svelte';
|
||||
import PinCodeResetModal from '$lib/modals/PinCodeResetModal.svelte';
|
||||
import { getAuthStatus } from '@immich/sdk';
|
||||
import { modalManager } from '@immich/ui';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
@@ -14,18 +13,17 @@
|
||||
hasPinCode = pinCode;
|
||||
});
|
||||
|
||||
const handleResetPINCode = async () => {
|
||||
const success = await modalManager.show(PinCodeResetModal, {});
|
||||
if (success) {
|
||||
hasPinCode = false;
|
||||
}
|
||||
const onUserPinCodeReset = () => {
|
||||
hasPinCode = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<OnEvents {onUserPinCodeReset} />
|
||||
|
||||
<section>
|
||||
{#if hasPinCode}
|
||||
<div in:fade={{ duration: 200 }}>
|
||||
<PinCodeChangeForm onForgot={handleResetPINCode} />
|
||||
<PinCodeChangeForm />
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade={{ duration: 200 }}>
|
||||
|
||||
@@ -52,6 +52,8 @@ export type Events = {
|
||||
TagUpdate: [TagResponseDto];
|
||||
TagDelete: [TreeNode];
|
||||
|
||||
UserPinCodeReset: [];
|
||||
|
||||
UserAdminCreate: [UserAdminResponseDto];
|
||||
UserAdminUpdate: [UserAdminResponseDto];
|
||||
UserAdminRestore: [UserAdminResponseDto];
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import ApiKeyPermissionsPicker from '$lib/components/ApiKeyPermissionsPicker.svelte';
|
||||
import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte';
|
||||
import { handleCreateApiKey } from '$lib/services/api-key.service';
|
||||
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 { t } from 'svelte-i18n';
|
||||
|
||||
@@ -15,11 +16,11 @@
|
||||
const isAllPermissions = $derived(selectedPermissions.length === Object.keys(Permission).length - 1);
|
||||
|
||||
const onSubmit = async () => {
|
||||
const success = await handleCreateApiKey({
|
||||
name,
|
||||
permissions: isAllPermissions ? [Permission.All] : selectedPermissions,
|
||||
});
|
||||
if (success) {
|
||||
const permissions = isAllPermissions ? [Permission.All] : selectedPermissions;
|
||||
const response = await handleCreateApiKey({ name, permissions });
|
||||
if (response) {
|
||||
// no nested modal
|
||||
void modalManager.show(ApiKeySecretModal, { secret: response.secret });
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createJob, ManualJobName } from '@immich/sdk';
|
||||
import { ConfirmModal, toastManager } from '@immich/ui';
|
||||
import { handleCreateJob } from '$lib/services/job.service';
|
||||
import { ManualJobName } from '@immich/sdk';
|
||||
import { FormModal } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
type Props = { onClose: (confirmed: boolean) => void };
|
||||
type Props = { onClose: () => void };
|
||||
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
@@ -20,42 +20,18 @@
|
||||
|
||||
let selectedJob: ComboBoxOption | undefined = $state(undefined);
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleCreate();
|
||||
};
|
||||
|
||||
const handleCreate = async () => {
|
||||
const onSubmit = async () => {
|
||||
if (!selectedJob) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
|
||||
toastManager.success($t('admin.job_created'));
|
||||
onClose(true);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_submit_job'));
|
||||
const success = await handleCreateJob({ name: selectedJob.value as ManualJobName });
|
||||
if (success) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<ConfirmModal
|
||||
confirmColor="primary"
|
||||
title={$t('admin.create_job')}
|
||||
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>
|
||||
<FormModal title={$t('admin.create_job')} submitText={$t('create')} disabled={!selectedJob} {onClose} {onSubmit}>
|
||||
<Combobox bind:selectedOption={selectedJob} label={$t('jobs')} {options} placeholder={$t('admin.search_jobs')} />
|
||||
</FormModal>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<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 { SettingInputFieldType } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createApiKey, Permission } from '@immich/sdk';
|
||||
import { Button, Modal, ModalBody, obtainiumBadge, Text } from '@immich/ui';
|
||||
import { handleCreateApiKey } from '$lib/services/api-key.service';
|
||||
import { Permission } from '@immich/sdk';
|
||||
import { Button, Field, Input, Modal, ModalBody, obtainiumBadge, Text } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
let inputUrl = $state(location.origin);
|
||||
let inputApiKey = $state('');
|
||||
@@ -12,72 +10,63 @@
|
||||
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`,
|
||||
);
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
const { secret } = await createApiKey({
|
||||
apiKeyCreateDto: {
|
||||
name: 'Obtainium',
|
||||
permissions: [Permission.ServerApkLinks],
|
||||
},
|
||||
});
|
||||
inputApiKey = secret;
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_create_api_key'));
|
||||
const response = await handleCreateApiKey({ name: 'Obtainium', permissions: [Permission.ServerApkLinks] });
|
||||
if (response) {
|
||||
inputApiKey = response.secret;
|
||||
}
|
||||
};
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
let { onClose }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Modal title={$t('obtainium_configurator')} size="medium" {onClose}>
|
||||
<ModalBody>
|
||||
<div>
|
||||
<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>
|
||||
<Text color="muted" size="small">{$t('obtainium_configurator_instructions')}</Text>
|
||||
|
||||
<div class="mt-2 flex gap-2 place-items-center place-content-center">
|
||||
<SettingInputField inputType={SettingInputFieldType.TEXT} label={$t('api_key')} bind:value={inputApiKey} />
|
||||
<Field label={$t('url')} class="mt-4">
|
||||
<Input bind:value={inputUrl} />
|
||||
</Field>
|
||||
|
||||
<div class="translate-y-[3px]">
|
||||
<Button size="small" onclick={() => handleCreate()}>{$t('create_api_key')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Field label={$t('api_key')} class="mt-4">
|
||||
<Input bind:value={inputApiKey} />
|
||||
</Field>
|
||||
|
||||
<SettingSelect
|
||||
label={$t('app_architecture_variant')}
|
||||
bind:value={archVariant}
|
||||
options={[
|
||||
{ value: 'arm64-v8a-release', text: 'arm64-v8a' },
|
||||
{ value: 'armeabi-v7a-release', text: 'armeabi-v7a' },
|
||||
{ value: 'release', text: 'universal' },
|
||||
{ value: 'x86_64-release', text: 'x86_64' },
|
||||
]}
|
||||
/>
|
||||
</form>
|
||||
|
||||
{#if inputUrl && inputApiKey && archVariant}
|
||||
<div class="content-center">
|
||||
<hr />
|
||||
<div class="flex place-items-center place-content-center">
|
||||
<a
|
||||
href={obtainiumLink}
|
||||
class="underline text-sm immich-form-label"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
id="obtainium-link"
|
||||
>
|
||||
<img class="pt-2 pr-5 h-20" alt="Get it on Obtainium" src={obtainiumBadge} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-end mt-2">
|
||||
<Button size="small" onclick={handleCreate}>{$t('create_api_key')}</Button>
|
||||
</div>
|
||||
|
||||
<SettingSelect
|
||||
label={$t('app_architecture_variant')}
|
||||
bind:value={archVariant}
|
||||
options={[
|
||||
{ value: 'arm64-v8a-release', text: 'arm64-v8a' },
|
||||
{ value: 'armeabi-v7a-release', text: 'armeabi-v7a' },
|
||||
{ value: 'release', text: 'universal' },
|
||||
{ value: 'x86_64-release', text: 'x86_64' },
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if inputUrl && inputApiKey && archVariant}
|
||||
<div class="content-center">
|
||||
<hr />
|
||||
<div class="flex place-items-center place-content-center">
|
||||
<a
|
||||
href={obtainiumLink}
|
||||
class="underline text-sm immich-form-label"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
id="obtainium-link"
|
||||
>
|
||||
<img class="pt-2 h-20" alt="Get it on Obtainium" src={obtainiumBadge} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { resetPinCode } from '@immich/sdk';
|
||||
import {
|
||||
Button,
|
||||
Field,
|
||||
HelperText,
|
||||
HStack,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
PasswordInput,
|
||||
Stack,
|
||||
Text,
|
||||
toastManager,
|
||||
} from '@immich/ui';
|
||||
import { handleResetPinCode } from '$lib/services/user.service';
|
||||
import { Field, FormModal, HelperText, Modal, ModalBody, PasswordInput, Stack, type ModalSize } from '@immich/ui';
|
||||
import { mdiLockReset } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
@@ -24,55 +11,35 @@
|
||||
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
let passwordLoginEnabled = $derived(featureFlagsManager.value.passwordLogin);
|
||||
let password = $state('');
|
||||
|
||||
const handleReset = async () => {
|
||||
try {
|
||||
await resetPinCode({ pinCodeResetDto: { password } });
|
||||
toastManager.success($t('pin_code_reset_successfully'));
|
||||
onClose(true);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.failed_to_reset_pin_code'));
|
||||
const onSubmit = async () => {
|
||||
const success = await handleResetPinCode({ password });
|
||||
if (success) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleReset();
|
||||
};
|
||||
const common = $derived({ title: $t('reset'), size: 'small' as ModalSize, icon: mdiLockReset, onClose });
|
||||
</script>
|
||||
|
||||
<Modal title={$t('reset_pin_code')} icon={mdiLockReset} size="small" {onClose}>
|
||||
<ModalBody>
|
||||
<form {onsubmit} autocomplete="off" id="reset-pin-form">
|
||||
<Stack gap={4}>
|
||||
<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" />
|
||||
<section>
|
||||
<Field label={$t('confirm_password')} required>
|
||||
<PasswordInput bind:value={password} autocomplete="current-password" />
|
||||
<HelperText>
|
||||
<Text color="muted">{$t('reset_pin_code_with_password')}</Text>
|
||||
</HelperText>
|
||||
</Field>
|
||||
</section>
|
||||
{/if}
|
||||
</Stack>
|
||||
</form>
|
||||
</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}
|
||||
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('close')}</Button>
|
||||
{/if}
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
{#if featureFlagsManager.value.passwordLogin === false}
|
||||
<FormModal {...common} submitColor="danger" submitText={$t('reset')} disabled={!password} {onSubmit}>
|
||||
<Stack gap={4}>
|
||||
<div>{$t('reset_pin_code_description')}</div>
|
||||
<hr class="my-2 h-px w-full border-0 bg-gray-200 dark:bg-gray-600" />
|
||||
<section>
|
||||
<Field label={$t('confirm_password')} required>
|
||||
<PasswordInput bind:value={password} autocomplete="current-password" />
|
||||
<HelperText color="muted">{$t('reset_pin_code_with_password')}</HelperText>
|
||||
</Field>
|
||||
</section>
|
||||
</Stack>
|
||||
</FormModal>
|
||||
{:else}
|
||||
<Modal {...common} closeOnBackdropClick>
|
||||
<ModalBody>
|
||||
<div>{$t('reset_pin_code_description')}</div>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import ApiKeyCreateModal from '$lib/modals/ApiKeyCreateModal.svelte';
|
||||
import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte';
|
||||
import ApiKeyUpdateModal from '$lib/modals/ApiKeyUpdateModal.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
@@ -56,14 +55,10 @@ export const handleCreateApiKey = async (dto: ApiKeyCreateDto) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiKey, secret } = await createApiKey({ apiKeyCreateDto: dto });
|
||||
const response = await createApiKey({ apiKeyCreateDto: dto });
|
||||
eventManager.emit('ApiKeyCreate', response.apiKey);
|
||||
|
||||
eventManager.emit('ApiKeyCreate', apiKey);
|
||||
|
||||
// no nested modal
|
||||
void modalManager.show(ApiKeySecretModal, { secret });
|
||||
|
||||
return true;
|
||||
return response;
|
||||
} catch (error) {
|
||||
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'),
|
||||
type: $t('command'),
|
||||
shortcuts: { shift: true, key: 'n' },
|
||||
onAction: async () => {
|
||||
await modalManager.show(JobCreateModal, {});
|
||||
},
|
||||
onAction: () => modalManager.show(JobCreateModal, {}),
|
||||
};
|
||||
|
||||
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'));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user