mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 05:46:43 +02:00
♻️ refactor: centralize global error handler in shared-ui
Extract setupGlobalErrorHandler() utility from contacts app and add to @manacore/shared-ui. Migrate 7 apps to use the shared implementation: calendar, chat, clock, contacts, matrix, picture, storage. Features: - Catches unhandled promise rejections with error classification - Handles offline/online network status changes - Built-in i18n (DE + EN) with customizable translations - Optional onAuthError callback for redirect handling - Returns cleanup function for proper unmounting
This commit is contained in:
parent
aca66b2014
commit
cdac341882
11 changed files with 307 additions and 119 deletions
|
|
@ -3,7 +3,7 @@
|
|||
import '$lib/i18n';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
import { AppLoadingSkeleton } from '$lib/components/skeletons';
|
||||
import { isLoading as i18nLoading } from 'svelte-i18n';
|
||||
import { onMount } from 'svelte';
|
||||
|
|
@ -13,10 +13,16 @@
|
|||
let loading = $state(true);
|
||||
let appReady = $derived(!loading && !$i18nLoading);
|
||||
|
||||
onMount(async () => {
|
||||
onMount(() => {
|
||||
// Setup global error handling
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
|
||||
theme.initialize();
|
||||
await authStore.initialize();
|
||||
loading = false;
|
||||
authStore.initialize().then(() => {
|
||||
loading = false;
|
||||
});
|
||||
|
||||
return cleanupErrorHandler;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@
|
|||
import '../app.css';
|
||||
import { onMount } from 'svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
onMount(() => {
|
||||
const cleanup = theme.initialize();
|
||||
return cleanup;
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
const cleanupTheme = theme.initialize();
|
||||
|
||||
return () => {
|
||||
cleanupErrorHandler();
|
||||
cleanupTheme();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,24 +5,34 @@
|
|||
import { theme } from '$lib/stores/theme.svelte';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { waitLocale } from '$lib/i18n';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
import { AppLoadingSkeleton } from '$lib/components/skeletons';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let loading = $state(true);
|
||||
|
||||
onMount(async () => {
|
||||
// Wait for locale to be loaded
|
||||
await waitLocale();
|
||||
onMount(() => {
|
||||
// Setup global error handling
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
|
||||
// Initialize theme
|
||||
theme.initialize();
|
||||
// Initialize async operations
|
||||
const init = async () => {
|
||||
// Wait for locale to be loaded
|
||||
await waitLocale();
|
||||
|
||||
// Initialize auth
|
||||
await authStore.initialize();
|
||||
// Initialize theme
|
||||
theme.initialize();
|
||||
|
||||
loading = false;
|
||||
// Initialize auth
|
||||
await authStore.initialize();
|
||||
|
||||
loading = false;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
return cleanupErrorHandler;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
import '../app.css';
|
||||
import '$lib/i18n'; // Initialize i18n early
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import { isLoading as i18nLoading } from 'svelte-i18n';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toastStore, ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
import { AppLoadingSkeleton } from '$lib/components/skeletons';
|
||||
|
||||
let { children } = $props();
|
||||
|
|
@ -16,73 +15,19 @@
|
|||
// Derived state: app is ready when auth is initialized AND i18n is loaded
|
||||
let appReady = $derived(!loading && !$i18nLoading);
|
||||
|
||||
/**
|
||||
* Global error handler for unhandled promise rejections and API errors
|
||||
*/
|
||||
function setupGlobalErrorHandling() {
|
||||
if (!browser) return;
|
||||
|
||||
// Handle unhandled promise rejections (e.g., failed API calls)
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
const error = event.reason;
|
||||
|
||||
// Extract error message
|
||||
let message = 'Ein unerwarteter Fehler ist aufgetreten';
|
||||
|
||||
if (error instanceof Error) {
|
||||
// Network errors
|
||||
if (error.message === 'Failed to fetch' || error.name === 'TypeError') {
|
||||
message = 'Netzwerkfehler: Server nicht erreichbar';
|
||||
}
|
||||
// Auth errors
|
||||
else if (
|
||||
error.message.includes('401') ||
|
||||
error.message.toLowerCase().includes('unauthorized')
|
||||
) {
|
||||
message = 'Sitzung abgelaufen. Bitte erneut anmelden.';
|
||||
}
|
||||
// Other API errors
|
||||
else if (error.message) {
|
||||
message = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// Show toast notification
|
||||
toastStore.error(message);
|
||||
|
||||
// Prevent default browser error handling
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Handle general JavaScript errors
|
||||
window.addEventListener('error', (event) => {
|
||||
// Only handle non-script errors (network failures for resources, etc.)
|
||||
if (event.message && !event.filename) {
|
||||
toastStore.error('Ein Fehler ist aufgetreten');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle offline/online status
|
||||
window.addEventListener('offline', () => {
|
||||
toastStore.warning('Keine Internetverbindung', 10000);
|
||||
});
|
||||
|
||||
window.addEventListener('online', () => {
|
||||
toastStore.success('Verbindung wiederhergestellt');
|
||||
});
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Setup global error handling
|
||||
setupGlobalErrorHandling();
|
||||
onMount(() => {
|
||||
// Setup global error handling (German translations by default)
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
|
||||
// Initialize theme
|
||||
theme.initialize();
|
||||
|
||||
// Initialize auth
|
||||
await authStore.initialize();
|
||||
authStore.initialize().then(() => {
|
||||
loading = false;
|
||||
});
|
||||
|
||||
loading = false;
|
||||
return cleanupErrorHandler;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import type { Snippet } from 'svelte';
|
||||
import { isLoading as i18nLoading, _ as t } from 'svelte-i18n';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
|
||||
interface Props {
|
||||
children: Snippet;
|
||||
|
|
@ -14,8 +14,13 @@
|
|||
let { children }: Props = $props();
|
||||
|
||||
onMount(() => {
|
||||
const cleanup = theme.initialize();
|
||||
return cleanup;
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
const cleanupTheme = theme.initialize();
|
||||
|
||||
return () => {
|
||||
cleanupErrorHandler();
|
||||
cleanupTheme();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import '../app.css';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
// Import and initialize theme
|
||||
|
|
@ -14,6 +14,9 @@
|
|||
let { children, data } = $props();
|
||||
|
||||
onMount(() => {
|
||||
// Setup global error handling
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
|
||||
// Initialize theme (applies CSS variables and loads from localStorage)
|
||||
const cleanupTheme = theme.initialize();
|
||||
|
||||
|
|
@ -21,6 +24,7 @@
|
|||
authStore.initialize();
|
||||
|
||||
return () => {
|
||||
cleanupErrorHandler();
|
||||
cleanupTheme();
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { locale } from 'svelte-i18n';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import { PillNavigation, setupGlobalErrorHandler } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import { theme } from '$lib/stores/theme.svelte';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
|
@ -140,31 +140,41 @@
|
|||
goto('/login');
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Initialize theme
|
||||
theme.initialize();
|
||||
onMount(() => {
|
||||
// Setup global error handling
|
||||
const cleanupErrorHandler = setupGlobalErrorHandler();
|
||||
|
||||
// Initialize auth
|
||||
await authStore.initialize();
|
||||
// Initialize async operations
|
||||
const init = async () => {
|
||||
// Initialize theme
|
||||
theme.initialize();
|
||||
|
||||
// Load user settings
|
||||
await userSettings.load();
|
||||
// Initialize auth
|
||||
await authStore.initialize();
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('storage-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
// Load user settings
|
||||
await userSettings.load();
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('storage-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
isCollapsed = true;
|
||||
collapsedStore.set(true);
|
||||
}
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('storage-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('storage-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
isCollapsed = true;
|
||||
collapsedStore.set(true);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
return cleanupErrorHandler;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue