mirror-immich/web/src/routes/auth/onboarding/+page.svelte

184 lines
6.1 KiB
Svelte

<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
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 { AppRoute, QueryParameter } from '$lib/constants';
import { OnboardingRole } from '$lib/models/onboarding-role';
import { retrieveServerConfig, retrieveSystemConfig, serverConfig } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store';
import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk';
import {
mdiCellphoneArrowDownVariant,
mdiCloudCheckOutline,
mdiHarddisk,
mdiIncognito,
mdiThemeLightDark,
mdiTranslate,
} from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
interface OnboardingStep {
name: string;
component:
| typeof OnboardingHello
| typeof OnboardingTheme
| typeof OnboardingStorageTemplate
| typeof OnboardingServerPrivacy
| typeof OnboardingUserPrivacy
| typeof OnboardingMobileApp
| typeof OnboardingLocale;
role: OnboardingRole;
title?: string;
icon?: string;
}
const onboardingSteps: OnboardingStep[] = $derived([
{ name: 'hello', component: OnboardingHello, role: OnboardingRole.USER },
{
name: 'theme',
component: OnboardingTheme,
role: OnboardingRole.USER,
title: $t('theme'),
icon: mdiThemeLightDark,
},
{
name: 'language',
component: OnboardingLocale,
role: OnboardingRole.USER,
title: $t('language'),
icon: mdiTranslate,
},
{
name: 'server_privacy',
component: OnboardingServerPrivacy,
role: OnboardingRole.SERVER,
title: $t('server_privacy'),
icon: mdiIncognito,
},
{
name: 'user_privacy',
component: OnboardingUserPrivacy,
role: OnboardingRole.USER,
title: $t('user_privacy'),
icon: mdiIncognito,
},
{
name: 'storage_template',
component: OnboardingStorageTemplate,
role: OnboardingRole.SERVER,
title: $t('admin.storage_template_settings'),
icon: mdiHarddisk,
},
{
name: 'backup',
component: OnboardingBackup,
role: OnboardingRole.SERVER,
title: $t('admin.backup_onboarding_title'),
icon: mdiCloudCheckOutline,
},
{
name: 'mobile_app',
component: OnboardingMobileApp,
role: OnboardingRole.USER,
title: $t('mobile_app'),
icon: mdiCellphoneArrowDownVariant,
},
]);
let index = $state(0);
let userRole = $derived($user.isAdmin && !$serverConfig.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER);
let onboardingStepCount = $derived(onboardingSteps.filter((step) => shouldRunStep(step.role, userRole)).length);
let onboardingProgress = $derived(
onboardingSteps.filter((step, i) => shouldRunStep(step.role, userRole) && i <= index).length - 1,
);
const shouldRunStep = (stepRole: OnboardingRole, userRole: OnboardingRole) => {
return (
stepRole === OnboardingRole.USER ||
(stepRole === OnboardingRole.SERVER && userRole === OnboardingRole.SERVER && !$serverConfig.isOnboarded)
);
};
$effect(() => {
const stepState = $page.url.searchParams.get('step');
const temporaryIndex = onboardingSteps.findIndex((step) => step.name === stepState);
index = temporaryIndex === -1 ? 0 : temporaryIndex;
});
const previousStepIndex = $derived(
onboardingSteps.findLastIndex((step, i) => shouldRunStep(step.role, userRole) && i < index),
);
const nextStepIndex = $derived(
onboardingSteps.findIndex((step, i) => shouldRunStep(step.role, userRole) && i > index),
);
const handleNextClicked = async () => {
if (nextStepIndex == -1) {
if ($user.isAdmin) {
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
await retrieveServerConfig();
}
await setUserOnboarding({
onboardingDto: { isOnboarded: true },
});
await goto(AppRoute.PHOTOS);
} else {
await goto(
`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[nextStepIndex].name}`,
);
}
};
const handlePrevious = async () => {
if (previousStepIndex === -1) {
return;
}
await goto(
`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[previousStepIndex].name}`,
);
};
onMount(async () => {
await retrieveSystemConfig();
});
const OnboardingStep = $derived(onboardingSteps[index].component);
</script>
<section id="onboarding-page" class="min-w-dvw flex min-h-dvh p-4">
<div class="flex flex-col w-full">
<div class=" bg-gray-300 dark:bg-gray-600 rounded-md h-2">
<div
class="progress-bar bg-primary h-2 rounded-md transition-all duration-200 ease-out"
style="width: {(onboardingProgress / onboardingStepCount) * 100}%"
></div>
</div>
<div class="py-8 flex place-content-center place-items-center m-auto w-[min(100%,_800px)]">
<OnboardingCard
title={onboardingSteps[index].title}
icon={onboardingSteps[index].icon}
onNext={handleNextClicked}
onPrevious={handlePrevious}
previousTitle={onboardingSteps[previousStepIndex]?.title}
nextTitle={onboardingSteps[nextStepIndex]?.title}
>
<OnboardingStep />
</OnboardingCard>
</div>
</div>
</section>