Initial import
This commit is contained in:
132
src/App.tsx
Executable file
132
src/App.tsx
Executable file
@@ -0,0 +1,132 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { getOrCreateClientId } from './app/main/clientIdentity';
|
||||
import { reportClientError } from './app/main/errorLogApi';
|
||||
import { AppShell } from './app/main';
|
||||
import { InitialLoadingOverlay } from './app/main/InitialLoadingOverlay';
|
||||
import { ReleasePendingMainModal } from './app/main/ReleasePendingMainModal';
|
||||
import { reportVisitorPageView } from './features/history/api';
|
||||
import { useAppStore } from './store';
|
||||
|
||||
const CHUNK_LOAD_RETRY_SESSION_KEY = 'ai-code-app.chunk-load-retried';
|
||||
const INITIAL_LOADING_MIN_VISIBLE_MS = 450;
|
||||
|
||||
function shouldRetryChunkLoad(errorMessage: string) {
|
||||
return /Failed to fetch dynamically imported module|Importing a module script failed|Load failed|ChunkLoadError/i.test(
|
||||
errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
function retryChunkLoadOnce(errorMessage: string) {
|
||||
if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!shouldRetryChunkLoad(errorMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sessionStorage.getItem(CHUNK_LOAD_RETRY_SESSION_KEY) === '1') {
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionStorage.setItem(CHUNK_LOAD_RETRY_SESSION_KEY, '1');
|
||||
window.location.reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const { currentPage } = useAppStore();
|
||||
const lastTrackedPageIdRef = useRef<string | null>(null);
|
||||
const [showInitialLoading, setShowInitialLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const handleError = (event: ErrorEvent) => {
|
||||
const reportedError = event.error instanceof Error ? event.error : null;
|
||||
const errorMessage = event.message || reportedError?.message || '클라이언트 오류가 발생했습니다.';
|
||||
|
||||
if (retryChunkLoadOnce(errorMessage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void reportClientError({
|
||||
errorType: 'window.error',
|
||||
errorName: reportedError?.name ?? null,
|
||||
errorMessage,
|
||||
stackTrace: reportedError?.stack ?? null,
|
||||
requestPath: `${window.location.pathname}${window.location.search}${window.location.hash}`,
|
||||
context: {
|
||||
filename: event.filename || null,
|
||||
line: event.lineno || null,
|
||||
column: event.colno || null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
|
||||
const reason = event.reason;
|
||||
const reportedError = reason instanceof Error ? reason : null;
|
||||
const errorMessage =
|
||||
reportedError?.message || (typeof reason === 'string' ? reason : '처리되지 않은 Promise 거절이 발생했습니다.');
|
||||
|
||||
if (retryChunkLoadOnce(errorMessage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void reportClientError({
|
||||
errorType: 'unhandledrejection',
|
||||
errorName: reportedError?.name ?? null,
|
||||
errorMessage,
|
||||
stackTrace: reportedError?.stack ?? null,
|
||||
requestPath: `${window.location.pathname}${window.location.search}${window.location.hash}`,
|
||||
context: {
|
||||
reasonType: typeof reason,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('error', handleError);
|
||||
window.addEventListener('unhandledrejection', handleUnhandledRejection);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('error', handleError);
|
||||
window.removeEventListener('unhandledrejection', handleUnhandledRejection);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getOrCreateClientId();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const hideTimer = window.setTimeout(() => {
|
||||
setShowInitialLoading(false);
|
||||
}, INITIAL_LOADING_MIN_VISIBLE_MS);
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(hideTimer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastTrackedPageIdRef.current === currentPage.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastTrackedPageIdRef.current = currentPage.id;
|
||||
void reportVisitorPageView(currentPage);
|
||||
}, [currentPage]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppShell />
|
||||
<ReleasePendingMainModal />
|
||||
{showInitialLoading ? <InitialLoadingOverlay /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user