Initial commit: code as received (Create/Anything export)
Insole-production time tracker exported from the Create/Anything AI platform. Baseline snapshot before any reverse-engineering or cleanup. - apps/mobile: Expo Router app (iOS/Android/web), the only workspace - publisher/: standalone OpenNext/AWS deploy tooling for the web side - Backend (/api/tasks, /api/logs + DB) lives remotely, not in this repo
This commit is contained in:
175
apps/mobile/App.web.tsx
Normal file
175
apps/mobile/App.web.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import { usePathname, useRouter } from 'expo-router';
|
||||
import { App } from 'expo-router/build/qualified-entry';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import './src/__create/polyfills';
|
||||
|
||||
import { ErrorBoundary } from './src/__create/ErrorBoundary';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { Toaster } from 'sonner-native';
|
||||
import { AlertModal } from './polyfills/web/alerts.web';
|
||||
import './global.css';
|
||||
|
||||
const RUNTIME_ERROR_PATTERNS = [
|
||||
/fetch failed/i,
|
||||
/networks*(error|request)/i,
|
||||
/failed to fetch/i,
|
||||
/load failed/i,
|
||||
/ECONNREFUSED/i,
|
||||
/ECONNRESET/i,
|
||||
/ETIMEDOUT/i,
|
||||
/ENOTFOUND/i,
|
||||
/ERR_CONNECTION/i,
|
||||
/aborted/i,
|
||||
/timeout/i,
|
||||
/socket hang up/i,
|
||||
/503\b/,
|
||||
/502\b/,
|
||||
/504\b/,
|
||||
/getaddrinfo/i,
|
||||
];
|
||||
|
||||
function isRuntimeError(msg: string) {
|
||||
return RUNTIME_ERROR_PATTERNS.some((p) => p.test(msg));
|
||||
}
|
||||
|
||||
function postErrorToParent(message: string, name: string, stack: string) {
|
||||
try {
|
||||
if (window.parent !== window) {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'sandbox:error:detected',
|
||||
error: { message, name, stack },
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const GlobalErrorReporter = () => {
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const errorHandler = (event: ErrorEvent) => {
|
||||
if (typeof event.preventDefault === 'function') event.preventDefault();
|
||||
console.error(event.error);
|
||||
|
||||
const error = event.error;
|
||||
const message = error?.message || event.message || 'Unknown error';
|
||||
if (!isRuntimeError(message)) {
|
||||
postErrorToParent(message, error?.name || 'Error', error?.stack || '');
|
||||
}
|
||||
};
|
||||
const unhandledRejectionHandler = (event: PromiseRejectionEvent) => {
|
||||
if (typeof event.preventDefault === 'function') event.preventDefault();
|
||||
const reason = event.reason;
|
||||
console.error('Unhandled promise rejection:', reason);
|
||||
|
||||
const message = reason?.message || String(reason || '');
|
||||
if (isRuntimeError(message)) return;
|
||||
const isCodeError =
|
||||
reason instanceof TypeError ||
|
||||
reason instanceof ReferenceError ||
|
||||
reason instanceof SyntaxError ||
|
||||
reason?.code === 'MODULE_RESOLVE_FAILED';
|
||||
if (!isCodeError) return;
|
||||
postErrorToParent(message, reason?.name || 'Error', reason?.stack || '');
|
||||
};
|
||||
window.addEventListener('error', errorHandler);
|
||||
window.addEventListener('unhandledrejection', unhandledRejectionHandler);
|
||||
return () => {
|
||||
window.removeEventListener('error', errorHandler);
|
||||
window.removeEventListener(
|
||||
'unhandledrejection',
|
||||
unhandledRejectionHandler
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
const Wrapper = memo(() => {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<SafeAreaProvider
|
||||
initialMetrics={{
|
||||
insets: { top: 64, bottom: 34, left: 0, right: 0 },
|
||||
frame: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: typeof window === 'undefined' ? 390 : window.innerWidth,
|
||||
height: typeof window === 'undefined' ? 844 : window.innerHeight,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<App />
|
||||
<GlobalErrorReporter />
|
||||
<Toaster />
|
||||
</SafeAreaProvider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
const healthyResponse = {
|
||||
type: 'sandbox:mobile:healthcheck:response',
|
||||
healthy: true,
|
||||
};
|
||||
|
||||
const useHandshakeParent = () => {
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data.type === 'sandbox:mobile:healthcheck') {
|
||||
window.parent.postMessage(healthyResponse, '*');
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', handleMessage);
|
||||
// Immediately respond to the parent window with a healthy response in
|
||||
// case we missed the healthcheck message
|
||||
window.parent.postMessage(healthyResponse, '*');
|
||||
return () => {
|
||||
window.removeEventListener('message', handleMessage);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
const CreateApp = () => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
useHandshakeParent();
|
||||
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (
|
||||
event.data.type === 'sandbox:navigation' &&
|
||||
event.data.pathname !== pathname
|
||||
) {
|
||||
router.push(event.data.pathname);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessage);
|
||||
window.parent.postMessage({ type: 'sandbox:mobile:ready' }, '*');
|
||||
return () => {
|
||||
window.removeEventListener('message', handleMessage);
|
||||
};
|
||||
}, [router, pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'sandbox:mobile:navigation',
|
||||
pathname,
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper />
|
||||
<AlertModal />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateApp;
|
||||
Reference in New Issue
Block a user