immich/web/src/routes/+layout.svelte
Freddie Floydd b9ca68f6e4
chore(web): rename components to PascalCase (#28013)
chore: rename components to PascalCase
2026-04-21 12:29:42 -04:00

217 lines
7.0 KiB
Svelte

<script lang="ts">
import { afterNavigate, beforeNavigate } from '$app/navigation';
import { page } from '$app/state';
import { getPagesProvider, getSettingsProvider } from '$lib/commands';
import DownloadPanel from './DownloadPanel.svelte';
import ErrorLayout from './ErrorLayout.svelte';
import OnEvents from '$lib/components/OnEvents.svelte';
import NavigationLoadingBar from './NavigationLoadingBar.svelte';
import UploadPanel from './UploadPanel.svelte';
import VersionAnnouncement from './VersionAnnouncement.svelte';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
import ServerRestartingModal from '$lib/modals/ServerRestartingModal.svelte';
import { Route } from '$lib/route';
import { locale } from '$lib/stores/preferences.store';
import { sidebarStore } from '$lib/stores/sidebar.svelte';
import { closeWebsocketConnection, openWebsocketConnection, websocketStore } from '$lib/stores/websocket';
import { maintenanceShouldRedirect } from '$lib/utils/maintenance';
import { getServerConfig } from '@immich/sdk';
import {
CommandPaletteProvider,
CORE_PAGE_COMMANDS,
defaultProvider,
MOBILE_APP_COMMANDS,
modalManager,
OTHER_SITE_COMMANDS,
PROJECT_SUPPORT_COMMANDS,
ScreencastOverlay,
setLocale,
setTranslations,
SOCIAL_COMMANDS,
Theme,
themeManager,
toastManager,
TooltipProvider,
} from '@immich/ui';
import { onMount, type Snippet } from 'svelte';
import { t } from 'svelte-i18n';
import { get } from 'svelte/store';
import '../app.css';
interface Props {
children?: Snippet;
}
$effect(() => {
setTranslations({
cancel: $t('cancel'),
close: $t('close'),
confirm: $t('confirm'),
expand: $t('expand'),
collapse: $t('collapse'),
search_placeholder: $t('search'),
search_no_results: $t('no_results'),
prompt_default: $t('are_you_sure_to_do_this'),
show_password: $t('show_password'),
hide_password: $t('hide_password'),
dark_theme: themeManager.value === Theme.Dark ? $t('light_theme') : $t('dark_theme'),
open_menu: $t('open'),
command_palette_prompt_default: $t('command_palette_prompt'),
command_palette_to_select: $t('command_palette_to_select'),
command_palette_to_navigate: $t('command_palette_to_navigate'),
command_palette_to_close: $t('command_palette_to_close'),
command_palette_to_show_all: $t('command_palette_to_show_all'),
navigate_next: $t('next'),
navigate_previous: $t('previous'),
open_calendar: $t('open_calendar'),
toast_success_title: $t('success'),
toast_info_title: $t('info'),
toast_warning_title: $t('warning'),
toast_danger_title: $t('error'),
save: $t('save'),
supporter: $t('supporter'),
});
});
$effect(() => setLocale($locale));
let { children }: Props = $props();
let showNavigationLoadingBar = $state(false);
toastManager.setOptions({ class: 'top-16 fixed' });
onMount(() => {
const element = document.querySelector('#stencil');
element?.remove();
// if the browser theme changes, changes the Immich theme too
});
eventManager.emit('AppInit');
beforeNavigate(({ from, to }) => {
if (sidebarStore.isOpen) {
sidebarStore.reset();
}
const fromRouteId = from?.route?.id;
const toRouteId = to?.route?.id;
const sameRouteTransition = fromRouteId && toRouteId && fromRouteId === toRouteId;
if (sameRouteTransition) {
return;
}
eventManager.emit('AppNavigate');
showNavigationLoadingBar = true;
});
afterNavigate(() => {
showNavigationLoadingBar = false;
});
const { serverRestarting } = websocketStore;
$effect.pre(() => {
if (authManager.authenticated || $serverRestarting || page.url.pathname.startsWith(Route.maintenanceMode())) {
openWebsocketConnection();
} else {
closeWebsocketConnection();
}
});
serverRestarting.subscribe((isRestarting) => {
if (!isRestarting) {
return;
}
if (maintenanceShouldRedirect(isRestarting.isMaintenanceMode, location)) {
modalManager.show(ServerRestartingModal, {}).catch((error) => console.error('Error [ServerRestartBox]:', error));
}
});
const onWebsocketConnect = async () => {
const isRestarting = get(serverRestarting);
if (isRestarting && maintenanceShouldRedirect(isRestarting.isMaintenanceMode, location)) {
const { maintenanceMode } = await getServerConfig();
if (maintenanceMode === isRestarting.isMaintenanceMode) {
location.reload();
}
}
};
</script>
<OnEvents {onWebsocketConnect} />
<VersionAnnouncement />
<svelte:head>
<title>{page.data.meta?.title || 'Web'} - Immich</title>
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
<meta name="theme-color" content="white" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="black" media="(prefers-color-scheme: dark)" />
{#if page.data.meta}
<meta name="description" content={page.data.meta.description} />
<!-- Facebook Meta Tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content={page.data.meta.title} />
<meta property="og:description" content={page.data.meta.description} />
{#if page.data.meta.imageUrl}
<meta
property="og:image"
content={new URL(
page.data.meta.imageUrl,
serverConfigManager.value.externalDomain || globalThis.location.origin,
).href}
/>
{/if}
<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={page.data.meta.title} />
<meta name="twitter:description" content={page.data.meta.description} />
{#if page.data.meta.imageUrl}
<meta
name="twitter:image"
content={new URL(
page.data.meta.imageUrl,
serverConfigManager.value.externalDomain || globalThis.location.origin,
).href}
/>
{/if}
{/if}
</svelte:head>
<TooltipProvider>
{#if page.data.error}
<ErrorLayout error={page.data.error}></ErrorLayout>
{:else}
{@render children?.()}
{/if}
{#if showNavigationLoadingBar}
<NavigationLoadingBar />
{/if}
<DownloadPanel />
<UploadPanel />
<ScreencastOverlay />
<CommandPaletteProvider
providers={[
getPagesProvider($t),
getSettingsProvider($t),
defaultProvider({ name: $t('documentation'), types: ['doc', 'documentation'], actions: CORE_PAGE_COMMANDS }),
defaultProvider({ name: $t('support'), actions: PROJECT_SUPPORT_COMMANDS }),
defaultProvider({ name: 'Socials', types: ['social', 'socials'], actions: SOCIAL_COMMANDS }),
defaultProvider({ name: $t('mobile_app'), actions: MOBILE_APP_COMMANDS }),
defaultProvider({ name: 'Sites', types: ['site', 'sites'], actions: OTHER_SITE_COMMANDS }),
]}
/>
</TooltipProvider>