/**
* WordPress dependencies
*/
import { select as doSelect, useDispatch } from '@safe-wordpress/data';
import { useCallback, useEffect } from '@safe-wordpress/element';
import { _x } from '@safe-wordpress/i18n';
/**
* External dependencies
*/
import clsx from 'clsx';
import { BigMessage, LoadingAnimation } from '@nab/components';
import type {
AlternativeIndex,
HeatmapErrorRationale,
HeatmapFrameAction,
} from '@nab/types';
/**
* Internal dependencies
*/
import './style.scss';
import {
useActiveAlternative,
useActiveFilter,
useActiveMode,
useActiveResolution,
useDisabledFilterOptions,
useHasProcessedResults,
useHeatmapIntensity,
useHeatmapOpacity,
useInteractivity,
useIsProcessingResults,
useProcessedResultsStatus,
useRawResultsStatus,
} from '../hooks';
import { store as NAB_HEATMAP } from '../../store';
export type ViewProps = {
readonly className: string;
readonly alternative: AlternativeIndex;
readonly contentUrl?: string;
};
export const View = ( {
className,
alternative,
contentUrl,
}: ViewProps ): JSX.Element => {
const [ activeAlternative ] = useActiveAlternative();
useProcessResultsEffect( alternative );
useRenderResultsEffect( alternative );
useOpacityChangeEffect( alternative );
useIntensityChangeEffect( alternative );
useInteractivityChangeEffect( alternative );
const [ resolution ] = useActiveResolution();
const awaitRendererScript = useRendererScriptWaiter( alternative );
const isHidden = alternative !== activeAlternative;
return (
);
};
// ============
// HELPER VIEWS
// ============
const FeedbackMessage = () => {
const isProcessingData = useIsProcessingResults();
const hasProcessedResults = useHasProcessedResults();
const [ status ] = useRawResultsStatus();
if (
status.mode === 'still-loading' ||
status.mode === 'canceling' ||
status.mode === 'ready'
) {
if ( ! isProcessingData || hasProcessedResults ) {
return null;
}
return (
);
}
if ( 'error' === status.mode ) {
return (
{ 'unreachable-servers' === status.rationale && (
{ _x(
'If you’re using an adblocker on your browser, please disable it as it’s probably blocking Nelio A/B Testing’s requests, thus preventing the plugin from pulling your heatmap data.',
'user',
'nelio-ab-testing'
) }
) }
);
}
return (
);
};
// =====
// HOOKS
// =====
const useRendererScriptWaiter = ( alternative: AlternativeIndex ) => {
const { setIFrameStatus } = useDispatch( NAB_HEATMAP );
return () =>
void setTimeout( () => {
const { getIFrameStatus } = doSelect( NAB_HEATMAP );
const status = getIFrameStatus( alternative );
const isWaitingScript = 'waiting-script' === status;
if ( isWaitingScript ) {
void setIFrameStatus( alternative, 'script-not-found' );
}
}, 30000 );
};
const useProcessResultsEffect = ( alternative: AlternativeIndex ) => {
const postMessage = usePostMessage( alternative );
const [ activeAlternative ] = useActiveAlternative();
const [ mode ] = useActiveMode();
const status = useProcessedResultsStatus();
useEffect( () => {
if ( activeAlternative !== alternative ) {
return;
}
if ( 'outdated' !== status ) {
return;
}
postMessage( {
plugin: 'nelio-ab-testing',
type: 'process-results',
mode,
} );
}, [ activeAlternative, alternative, status, mode, postMessage ] );
};
const useRenderResultsEffect = ( alternative: AlternativeIndex ) => {
const [ activeAlternative ] = useActiveAlternative();
const postMessage = usePostMessage( alternative );
const [ filter ] = useActiveFilter();
const [ mode ] = useActiveMode();
const [ resolution ] = useActiveResolution();
const [ disabledFilterOptions ] = useDisabledFilterOptions();
const status = useProcessedResultsStatus();
useEffect( () => {
if ( activeAlternative !== alternative ) {
return;
}
if ( status !== 'ready' && status !== 'outdated' ) {
return;
}
postMessage( {
plugin: 'nelio-ab-testing',
type: 'render-results',
mode,
} );
}, [
activeAlternative,
alternative,
disabledFilterOptions,
filter,
mode,
resolution,
status,
postMessage,
] );
};
const useOpacityChangeEffect = ( alternative: AlternativeIndex ) => {
const postMessage = usePostMessage( alternative );
const [ mode ] = useActiveMode();
const [ opacity ] = useHeatmapOpacity();
const [ activeAlternative ] = useActiveAlternative();
useEffect( () => {
if ( activeAlternative !== alternative ) {
return;
}
postMessage( {
plugin: 'nelio-ab-testing',
type: 'update-opacity',
mode,
value: opacity,
} );
}, [ activeAlternative, alternative, mode, opacity, postMessage ] );
};
const useIntensityChangeEffect = ( alternative: AlternativeIndex ) => {
const postMessage = usePostMessage( alternative );
const [ intensity ] = useHeatmapIntensity();
const [ activeAlternative ] = useActiveAlternative();
const [ mode ] = useActiveMode();
useEffect( () => {
if ( 'heatmap' !== mode ) {
return;
}
if ( activeAlternative !== alternative ) {
return;
}
postMessage( {
plugin: 'nelio-ab-testing',
type: 'update-intensity',
mode,
value: intensity,
} );
}, [ activeAlternative, alternative, intensity, mode, postMessage ] );
};
const useInteractivityChangeEffect = ( alternative: AlternativeIndex ) => {
const postMessage = usePostMessage( alternative );
const [ isInteractive ] = useInteractivity();
const [ activeAlternative ] = useActiveAlternative();
const [ mode ] = useActiveMode();
useEffect( () => {
if ( 'heatmap' !== mode && 'confetti' !== mode ) {
return;
}
if ( activeAlternative !== alternative ) {
return;
}
postMessage( {
plugin: 'nelio-ab-testing',
type: 'make-interactive',
mode,
value: isInteractive,
} );
}, [ activeAlternative, alternative, isInteractive, mode, postMessage ] );
};
const usePostMessage = ( alternative: AlternativeIndex ) =>
useCallback(
( action: HeatmapFrameAction ) => {
const id = `nab-heatmap-results__iframe-${ alternative }`;
const iframe = document.getElementById( id ) as HTMLIFrameElement;
if ( iframe?.contentWindow?.postMessage ) {
iframe.contentWindow.postMessage( action );
}
},
[ alternative ]
);
// =======
// HELPERS
// =======
function getErrorMessage( rationale: HeatmapErrorRationale ) {
switch ( rationale ) {
case 'no-script':
return _x( 'Unable to render heatmap', 'text', 'nelio-ab-testing' );
case 'experiment-not-found':
return _x( 'Test not found', 'text', 'nelio-ab-testing' );
case 'alternative-not-found':
return _x( 'Page not found', 'text', 'nelio-ab-testing' );
case 'unreachable-servers':
return _x(
'There was an error while retrieving data from the cloud',
'text',
'nelio-ab-testing'
);
case 'stuck-empty-results':
case 'heatmap-data-not-available':
return _x(
'Heatmap data is currently unavailable',
'text',
'nelio-ab-testing'
);
case 'no-heatmap-data':
return _x( 'No heatmap data', 'text', 'nelio-ab-testing' );
case 'unknown':
return _x( 'Something went wrong', 'text', 'nelio-ab-testing' );
}
}
function getErrorIcon( rationale: HeatmapErrorRationale ): 'crash' | 'info' {
switch ( rationale ) {
case 'no-script':
case 'experiment-not-found':
case 'alternative-not-found':
case 'unreachable-servers':
case 'unknown':
return 'crash';
case 'stuck-empty-results':
case 'heatmap-data-not-available':
case 'no-heatmap-data':
return 'info';
}
}