Files
twenty/packages/twenty-front-component-renderer/src/host/components/FrontComponentRenderer.tsx
T
Raphaël Bosi a3beea893d Revert the external link confirmation popup for front components (#23567)
Reverts #23270 and #23404.

Links in front components navigate natively again, with no confirmation
popup and no per-app trusted-origins state in localStorage.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23567?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-30 12:55:55 +00:00

141 lines
5.6 KiB
TypeScript

import { ROOT_CONTAINER_STYLE } from '@/host/constants/RootContainerStyle';
import { FrontComponentGeometryTrackerContext } from '@/host/contexts/FrontComponentGeometryTrackerContext';
import { createGeometryTracker } from '@/host/utils/createGeometryTracker';
import { FrontComponentConfirmationModalResultEffect } from '@/remote/components/FrontComponentConfirmationModalResultEffect';
import { FrontComponentErrorEffect } from '@/remote/components/FrontComponentErrorEffect';
import { FrontComponentGeometryTrackerEffect } from '@/remote/components/FrontComponentGeometryTrackerEffect';
import { FrontComponentInitializeHostCommunicationApiEffect } from '@/remote/components/FrontComponentInitializeHostCommunicationApiEffect';
import { FrontComponentUpdateContextEffect } from '@/remote/components/FrontComponentUpdateContextEffect';
import { FrontComponentUpdateHostCommunicationApiEffect } from '@/remote/components/FrontComponentUpdateHostCommunicationApiEffect';
import { type FrontComponentHostCommunicationApi } from '@/types/FrontComponentHostCommunicationApi';
import { type FrontComponentThread } from '@/types/FrontComponentThread';
import { type SdkClientUrls } from '@/types/SdkClientUrls';
import { type FrontComponentExecutionContext } from 'twenty-sdk/front-component';
import {
type RemoteReceiver,
RemoteRootRenderer,
} from '@remote-dom/react/host';
import { type ReactNode, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { isDefined } from 'twenty-shared/utils';
import { ThemeProvider } from 'twenty-ui/theme-constants';
import { FrontComponentWorkerEffect } from '../../remote/components/FrontComponentWorkerEffect';
import { componentRegistry } from '../generated/host-component-registry';
import { createFallbackComponentRegistry } from '../utils/createFallbackComponentRegistry';
import { FrontComponentErrorBox } from './FrontComponentErrorBox';
const fallbackComponentRegistry =
createFallbackComponentRegistry(componentRegistry);
type FrontComponentRendererProps = {
componentUrl: string;
applicationAccessToken?: string;
apiUrl?: string;
functionsBaseUrl?: string;
sdkClientUrls?: SdkClientUrls;
applicationVariables?: Record<string, string>;
executionContext: FrontComponentExecutionContext;
frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi;
onError: (error?: Error) => void;
colorScheme: 'light' | 'dark';
loadingFallback?: ReactNode;
};
export const FrontComponentRenderer = ({
componentUrl,
applicationAccessToken,
apiUrl,
functionsBaseUrl,
sdkClientUrls,
applicationVariables,
executionContext,
frontComponentHostCommunicationApi,
onError,
colorScheme,
loadingFallback,
}: FrontComponentRendererProps) => {
const [receiver, setReceiver] = useState<RemoteReceiver | null>(null);
const [thread, setThread] = useState<FrontComponentThread | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isExecutionContextInitialized, setIsExecutionContextInitialized] =
useState(false);
const [geometryTracker] = useState(() => createGeometryTracker());
const isReady = isDefined(receiver) && isExecutionContextInitialized;
return (
<FrontComponentGeometryTrackerContext.Provider value={geometryTracker}>
<div ref={geometryTracker.setRoot} style={ROOT_CONTAINER_STYLE}>
<FrontComponentWorkerEffect
componentUrl={componentUrl}
applicationAccessToken={applicationAccessToken}
apiUrl={apiUrl}
functionsBaseUrl={functionsBaseUrl}
sdkClientUrls={sdkClientUrls}
applicationVariables={applicationVariables}
geometryTracker={geometryTracker}
setReceiver={setReceiver}
setThread={setThread}
setError={setError}
/>
{isDefined(error) && (
<ThemeProvider colorScheme={colorScheme}>
<FrontComponentErrorEffect error={error} onError={onError} />
<FrontComponentErrorBox error={error} />
</ThemeProvider>
)}
{isDefined(thread) && (
<>
<FrontComponentUpdateHostCommunicationApiEffect
thread={thread}
frontComponentHostCommunicationApi={
frontComponentHostCommunicationApi
}
/>
<FrontComponentInitializeHostCommunicationApiEffect
thread={thread}
/>
<FrontComponentGeometryTrackerEffect
thread={thread}
geometryTracker={geometryTracker}
/>
<FrontComponentUpdateContextEffect
thread={thread}
executionContext={executionContext}
onExecutionContextInitialized={() =>
setIsExecutionContextInitialized(true)
}
/>
<FrontComponentConfirmationModalResultEffect
thread={thread}
frontComponentId={executionContext.frontComponentId}
onError={setError}
/>
</>
)}
{!isDefined(error) && !isReady && loadingFallback}
{isReady && (
<ThemeProvider colorScheme={colorScheme}>
<ErrorBoundary
onError={setError}
onReset={() => setError(null)}
resetKeys={[componentUrl]}
fallbackRender={() => null}
>
<RemoteRootRenderer
receiver={receiver}
components={fallbackComponentRegistry}
/>
</ErrorBoundary>
</ThemeProvider>
)}
</div>
</FrontComponentGeometryTrackerContext.Provider>
);
};