mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor(web): co-locate single-use components in /routes (#27921)
* co-locate single use components to /routes * revert accidentally changed paths * fix mangled path * fmt * fix accidentally moved multi-use components
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import OnboardingBackup from '$lib/components/onboarding-page/onboarding-backup.svelte';
|
||||
import OnboardingCard from '$lib/components/onboarding-page/onboarding-card.svelte';
|
||||
import OnboardingHello from '$lib/components/onboarding-page/onboarding-hello.svelte';
|
||||
import OnboardingLocale from '$lib/components/onboarding-page/onboarding-language.svelte';
|
||||
import OnboardingMobileApp from '$lib/components/onboarding-page/onboarding-mobile-app.svelte';
|
||||
import OnboardingServerPrivacy from '$lib/components/onboarding-page/onboarding-server-privacy.svelte';
|
||||
import OnboardingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte';
|
||||
import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte';
|
||||
import OnboardingUserPrivacy from '$lib/components/onboarding-page/onboarding-user-privacy.svelte';
|
||||
import OnboardingBackup from './onboarding-backup.svelte';
|
||||
import OnboardingCard from './onboarding-card.svelte';
|
||||
import OnboardingHello from './onboarding-hello.svelte';
|
||||
import OnboardingLocale from './onboarding-language.svelte';
|
||||
import OnboardingMobileApp from './onboarding-mobile-app.svelte';
|
||||
import OnboardingServerPrivacy from './onboarding-server-privacy.svelte';
|
||||
import OnboardingStorageTemplate from './onboarding-storage-template.svelte';
|
||||
import OnboardingTheme from './onboarding-theme.svelte';
|
||||
import OnboardingUserPrivacy from './onboarding-user-privacy.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||
import { Icon, Link, Stack } from '@immich/ui';
|
||||
import { mdiAlertCircleOutline } from '@mdi/js';
|
||||
import type { Translations } from 'svelte-i18n';
|
||||
|
||||
const messageKeys = [
|
||||
'admin.backup_onboarding_3_description',
|
||||
'admin.backup_onboarding_2_description',
|
||||
'admin.backup_onboarding_1_description',
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Stack gap={2}>
|
||||
<div class="flex items-start gap-4 p-6 my-10 bg-gray-100 dark:bg-gray-800/40 rounded-xl border border-gray-700/50">
|
||||
<Icon icon={mdiAlertCircleOutline} size="36" class="text-warning shrink-0 mt-0.5" />
|
||||
<div class="text-gray-800 dark:text-gray-300 leading-relaxed">
|
||||
<FormatMessage key="admin.backup_onboarding_description">
|
||||
{#snippet children({ message })}
|
||||
<Link href="https://www.backblaze.com/blog/the-3-2-1-backup-strategy/">
|
||||
{message}
|
||||
</Link>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<h2 class="mb-6"><FormatMessage key="admin.backup_onboarding_parts_title" /></h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
{#each messageKeys as keyString, index (index)}
|
||||
<div class="flex items-start gap-6">
|
||||
<div class="shrink-0 w-12 h-12 rounded-full bg-primary/90 flex items-center justify-center">
|
||||
<span class="text-light text-xl font-semibold">{3 - index}</span>
|
||||
</div>
|
||||
<div class="leading-relaxed pt-2">
|
||||
<FormatMessage key={keyString as Translations} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mt-4">
|
||||
<p>
|
||||
<FormatMessage key="admin.backup_onboarding_footer">
|
||||
{#snippet children({ message })}
|
||||
<Link href="https://docs.immich.app/administration/backup-and-restore/">{message}</Link>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
</div>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { Button, Icon } from '@immich/ui';
|
||||
import { mdiArrowLeft, mdiArrowRight, mdiCheck } from '@mdi/js';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
interface Props {
|
||||
title?: string | undefined;
|
||||
icon?: string | undefined;
|
||||
children?: Snippet;
|
||||
previousTitle?: string | undefined;
|
||||
nextTitle?: string | undefined;
|
||||
onNext?: () => void;
|
||||
onPrevious?: () => void;
|
||||
onLeave?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
title = undefined,
|
||||
icon = undefined,
|
||||
children,
|
||||
previousTitle,
|
||||
nextTitle,
|
||||
onLeave,
|
||||
onNext,
|
||||
onPrevious,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="onboarding-card"
|
||||
class="flex w-full max-w-4xl flex-col gap-4 rounded-3xl border-2 border-gray-500 px-8 py-8 dark:border-gray-700 dark:bg-immich-dark-gray text-black dark:text-immich-dark-fg bg-gray-50"
|
||||
in:fade={{ duration: 250 }}
|
||||
>
|
||||
{#if title || icon}
|
||||
<div class="flex gap-2 items-center justify-center w-fit">
|
||||
{#if icon}
|
||||
<Icon {icon} size="30" class="text-primary" />
|
||||
{/if}
|
||||
{#if title}
|
||||
<p class="text-xl text-primary font-medium">
|
||||
{title}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
|
||||
<div class="flex pt-4">
|
||||
{#if previousTitle}
|
||||
<div class="w-full flex place-content-start">
|
||||
<Button
|
||||
shape="round"
|
||||
leadingIcon={mdiArrowLeft}
|
||||
class="flex gap-2 place-content-center"
|
||||
onclick={() => {
|
||||
onLeave?.();
|
||||
onPrevious?.();
|
||||
}}
|
||||
>
|
||||
<p>{previousTitle}</p>
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex w-full place-content-end">
|
||||
<Button
|
||||
shape="round"
|
||||
trailingIcon={nextTitle ? mdiArrowRight : mdiCheck}
|
||||
onclick={() => {
|
||||
onLeave?.();
|
||||
onNext?.();
|
||||
}}
|
||||
>
|
||||
<span class="flex place-content-center place-items-center gap-2">
|
||||
{nextTitle ?? $t('done')}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||
import { OnboardingRole } from '$lib/types';
|
||||
import { Logo } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
let userRole = $derived(
|
||||
authManager.user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="gap-4">
|
||||
<Logo variant="icon" size="giant" class="mb-2" />
|
||||
<p class="font-medium mb-6 text-6xl text-primary">
|
||||
{$t('onboarding_welcome_user', { values: { user: authManager.user.name } })}
|
||||
</p>
|
||||
<p class="text-3xl pb-6 font-light">
|
||||
{userRole == OnboardingRole.SERVER
|
||||
? $t('onboarding_server_welcome_description')
|
||||
: $t('onboarding_user_welcome_description')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import SettingsLanguageSelector from '$lib/components/shared-components/settings/settings-language-selector.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>
|
||||
{$t('onboarding_locale_description')}
|
||||
</p>
|
||||
|
||||
<SettingsLanguageSelector />
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import AppDownloadModal from '$lib/modals/AppDownloadModal.svelte';
|
||||
import ObtainiumConfigModal from '$lib/modals/ObtainiumConfigModal.svelte';
|
||||
import { Button, HStack, modalManager } from '@immich/ui';
|
||||
import { mdiCellphoneArrowDownVariant, mdiLinkEdit } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<p>{$t('mobile_app_download_onboarding_note')}</p>
|
||||
|
||||
<HStack>
|
||||
<Button
|
||||
size="medium"
|
||||
shape="semi-round"
|
||||
fullWidth
|
||||
onclick={() => modalManager.show(AppDownloadModal, {})}
|
||||
leadingIcon={mdiCellphoneArrowDownVariant}
|
||||
>
|
||||
{$t('app_stores')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="medium"
|
||||
shape="semi-round"
|
||||
fullWidth
|
||||
onclick={() => modalManager.show(ObtainiumConfigModal, {})}
|
||||
leadingIcon={mdiLinkEdit}
|
||||
>
|
||||
{$t('obtainium_configurator')}
|
||||
</Button>
|
||||
</HStack>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||
import { handleSystemConfigSave } from '$lib/services/system-config.service';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
const configToEdit = $state(systemConfigManager.cloneValue());
|
||||
|
||||
onDestroy(async () => {
|
||||
await handleSystemConfigSave({ map: configToEdit.map, newVersionCheck: configToEdit.newVersionCheck });
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>
|
||||
{$t('onboarding_privacy_description')}
|
||||
</p>
|
||||
|
||||
<SettingSwitch
|
||||
title={$t('admin.map_settings')}
|
||||
subtitle={$t('admin.map_implications')}
|
||||
bind:checked={configToEdit.map.enabled}
|
||||
/>
|
||||
<SettingSwitch
|
||||
title={$t('admin.version_check_settings')}
|
||||
subtitle={$t('admin.version_check_implications', { values: { server: 'version.immich.cloud' } })}
|
||||
bind:checked={configToEdit.newVersionCheck.enabled}
|
||||
/>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import StorageTemplateSettings from '$lib/components/admin-settings/StorageTemplateSettings.svelte';
|
||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { Link } from '@immich/ui';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<p>
|
||||
<FormatMessage key="admin.storage_template_onboarding_description_v2">
|
||||
{#snippet children({ message })}
|
||||
<Link href="https://docs.immich.app/administration/storage-template">{message}</Link>
|
||||
{/snippet}
|
||||
</FormatMessage>
|
||||
</p>
|
||||
|
||||
{#if authManager.authenticated}
|
||||
<StorageTemplateSettings minified duration={0} saveOnClose />
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
|
||||
import { Icon, themeManager, ThemePreference } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>{$t('onboarding_theme_description')}</p>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<button
|
||||
type="button"
|
||||
class="w-1/2 aspect-square bg-light dark:bg-dark rounded-3xl transition-all shadow-sm hover:shadow-xl border-[3px] border-immich-primary dark:border dark:border-transparent"
|
||||
onclick={() => themeManager.setPreference(ThemePreference.Light)}
|
||||
>
|
||||
<div
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-primary"
|
||||
>
|
||||
<Icon icon={sunPath} viewBox={sunViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('light')}</p>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-1/2 aspect-square bg-dark dark:bg-light rounded-3xl transition-all shadow-sm hover:shadow-xl dark:border-[3px] dark:border-immich-dark-primary border border-transparent"
|
||||
onclick={() => themeManager.setPreference(ThemePreference.Dark)}
|
||||
>
|
||||
<div
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-dark-primary"
|
||||
>
|
||||
<Icon icon={moonPath} viewBox={moonViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('dark')}</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateMyPreferences } from '@immich/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
let gCastEnabled = $state(authManager.authenticated ? authManager.preferences.cast.gCastEnabled : false);
|
||||
|
||||
onDestroy(async () => {
|
||||
try {
|
||||
const response = await updateMyPreferences({ userPreferencesUpdateDto: { cast: { gCastEnabled } } });
|
||||
authManager.setPreferences(response);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_update_settings'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>
|
||||
{$t('onboarding_privacy_description')}
|
||||
</p>
|
||||
|
||||
<SettingSwitch title={$t('gcast_enabled')} subtitle={$t('gcast_enabled_description')} bind:checked={gCastEnabled} />
|
||||
</div>
|
||||
Reference in New Issue
Block a user