Current File : /home/digitaw/www/wp-content/updraft/plugins-old/simple-history/src/components/EventIPAddresses.jsx
import {
	Button,
	ExternalLink,
	Flex,
	Popover,
	__experimentalText as Text,
} from '@wordpress/components';
import {
	createInterpolateElement,
	useEffect,
	useState,
} from '@wordpress/element';
import { __, _n } from '@wordpress/i18n';
import { close } from '@wordpress/icons';
import { EventHeaderItem } from './EventHeaderItem';

const keysAndValues = [
	{
		key: 'hostname',
		label: __( 'Hostname:', 'simple-history' ),
	},
	{
		key: 'org',
		label: __( 'Org:', 'simple-history' ),
	},
	{
		key: 'city',
		label: __( 'City:', 'simple-history' ),
	},
	{
		key: 'region',
		label: __( 'Region:', 'simple-history' ),
	},
	{
		key: 'country',
		label: __( 'Country:', 'simple-history' ),
	},
	{
		key: 'loc',
		label: __( 'Location:', 'simple-history' ),
	},
];

/**
 * Renders a link to an IP address.
 *
 * @param {Object} ipAddressProps
 */
function IPAddressLink( ipAddressProps ) {
	const { header, ipAddress, mapsApiKey, hasExtendedSettingsAddOn } =
		ipAddressProps;
	const [ showPopover, setShowPopover ] = useState( false );
	const [ isLoadingIpInfo, setIsLoadingIpInfo ] = useState( false );
	const [ ipInfoResult, setIpInfoResult ] = useState();

	// The ip address may be anonymized. In that case we need to change the last ".x" to ".0".
	// This is because the IP address is anonymized by setting the last octet to "x".
	// We need to change that to "0" to make the IP address valid.
	const ipAddressUnanonymized = ipAddress.replace( /\.x$/, '.0' );
	const ipInfoURL = `https://ipinfo.io/${ ipAddressUnanonymized }`;

	/**
	 * Load ip info from ipinfo.io.
	 */
	useEffect( () => {
		async function fetchData() {
			const response = await fetch( ipInfoURL, {
				method: 'GET',
				headers: {
					Accept: 'application/json',
				},
			} );

			const json = await response.json();

			setIpInfoResult( json );
			setIsLoadingIpInfo( false );
		}

		// Bail if we are not loading ip info.
		if ( ! isLoadingIpInfo ) {
			return;
		}

		fetchData();
	}, [ isLoadingIpInfo, ipAddress, ipInfoURL ] );

	/**
	 * Show the popover when the user clicks the IP address.
	 *
	 * @param {MouseEvent} clickEvt
	 */
	const handleClick = ( clickEvt ) => {
		const isClickOnPopoverArea = clickEvt.target.nodeName !== 'BUTTON';

		// Bail if already showing popover and we click inside the popover.
		// Seems like the click event is also triggered when clicking anywhere inside the popover,
		// even though the popover is rendered outside the button.
		if ( isClickOnPopoverArea ) {
			return;
		}

		// If we are already showing the popover, then hide it.
		if ( showPopover ) {
			setShowPopover( false );
			return;
		}

		// Show the popover and start loading ip info.
		setShowPopover( true );
		setIsLoadingIpInfo( true );
	};

	const bogonAddressText = createInterpolateElement(
		__(
			'That IP address does not seem like a public one. It is probably a <a>bogon ip address</a>.',
			'simple-history'
		),
		{
			a: (
				<ExternalLink
					href="https://ipinfo.io/bogon"
					target="_blank"
					rel="noopener noreferrer"
				/>
			),
		}
	);

	const map =
		mapsApiKey && ! ipInfoResult?.bogon && ipInfoResult?.loc ? (
			<tr>
				<td colSpan={ 2 }>
					<a
						href={ `https://www.google.com/maps/place/${ ipInfoResult.loc }/@${ ipInfoResult.loc },6z` }
						target="_blank"
						rel="noopener noreferrer"
					>
						<img
							src={ `https://maps.googleapis.com/maps/api/staticmap?center=${ ipInfoResult.loc }&zoom=7&size=350x100&scale=2&sensor=false&key=${ mapsApiKey }` }
							width="350"
							height="100"
							alt="Google Map"
						/>
					</a>
				</td>
			</tr>
		) : null;

	const upsellText = hasExtendedSettingsAddOn ? null : (
		<>
			<div
				style={ {
					display: 'grid',
					placeItems: 'center',
					width: '100%',
					height: 100,
					// TODO: Path to image.
					backgroundImage:
						'url("/wp-content/plugins/simple-history/assets/images/map-img-blur.jpg")',
					backgroundSize: 'cover',
					padding: '1rem',
				} }
			>
				<Text>
					{ createInterpolateElement(
						__(
							'See the location of the IP address on a map with the <a>Extended Settings</a> add-on.',
							'simple-history'
						),
						{
							a: (
								<ExternalLink
									href="https://simple-history.com/add-ons/extended-settings/?utm_source=plugin&utm_medium=link&utm_campaign=ipinfo#GoogleMaps"
									target="_blank"
									rel="noopener noreferrer"
								/>
							),
						}
					) }
				</Text>
			</div>
		</>
	);

	const loadedIpInfoText = ipInfoResult ? (
		<>
			{ upsellText }
			<table className="SimpleHistoryIpInfoDropin__ipInfoTable">
				<tbody>
					{ map }
					<tr>
						<td className="SimpleHistoryIpInfoDropin__ipInfoTable__key">
							{ __( 'IP address:', 'simple-history' ) }
						</td>
						<td>{ ipAddress }</td>
					</tr>

					<tr>
						<td className="SimpleHistoryIpInfoDropin__ipInfoTable__key">
							{ __( 'Header:', 'simple-history' ) }
						</td>
						<td>
							<code>{ header }</code>
						</td>
					</tr>

					{ ipInfoResult.bogon ? (
						<tr>
							<td className="SimpleHistoryIpInfoDropin__ipInfoTable__key">
								{ __( 'Error:', 'simple-history' ) }
							</td>
							<td>{ bogonAddressText }</td>
						</tr>
					) : null }

					{ /* Show values from ipinfo.io */ }
					{ keysAndValues.map( ( keyAndValue ) => {
						const { key, label } = keyAndValue;
						const value = ipInfoResult[ key ];

						if ( ! value ) {
							return null;
						}

						return (
							<tr key={ key }>
								<td className="SimpleHistoryIpInfoDropin__ipInfoTable__key">
									{ label }
								</td>
								<td>{ value }</td>
							</tr>
						);
					} ) }
				</tbody>
			</table>
			<Text
				align="right"
				isBlock
				variant="muted"
				style={ { marginTop: 10 } }
			>
				{ createInterpolateElement(
					__(
						'IP info provided by <a>ipinfo.io</a>',
						'simple-history'
					),
					{
						a: (
							<ExternalLink
								href="https://ipinfo.io/"
								target="_blank"
								rel="noopener noreferrer"
							/>
						),
					}
				) }
			</Text>
		</>
	) : null;

	return (
		<Button title={ header } onClick={ handleClick } variant="link">
			{ ipAddress }

			{ showPopover ? (
				<Popover
					noArrow={ false }
					offset={ 10 }
					placement="top"
					animate={ true }
					shift={ true }
				>
					<div
						style={ {
							minWidth: 350,
							minHeight: 100,
							padding: 10,
							overflow: 'hidden',
						} }
					>
						<Flex align="start">
							<div>
								{ isLoadingIpInfo ? (
									<p>
										{ __(
											'Getting IP info…',
											'simple-history'
										) }
									</p>
								) : (
									<>{ loadedIpInfoText }</>
								) }
							</div>

							<Button
								icon={ close }
								onClick={ () => setShowPopover( false ) }
							/>
						</Flex>
					</div>
				</Popover>
			) : null }
		</Button>
	);
}

/**
 * Renders a list of IP addresses.
 *
 * @param {Object} props
 */
export function EventIPAddresses( props ) {
	const { event, mapsApiKey, hasExtendedSettingsAddOn } = props;
	const { ip_addresses: ipAddresses } = event;

	if ( ! ipAddresses ) {
		return null;
	}

	const ipAddressesCount = Object.keys( ipAddresses ).length;

	if ( ipAddressesCount === 0 ) {
		return null;
	}

	const ipAddressesLabel = _n(
		'IP address:',
		'IP addresses:',
		ipAddressesCount,
		'simple-history'
	);

	const IPAddressesText = [];
	let loopCount = 0;
	for ( const [ header, ipAddress ] of Object.entries( ipAddresses ) ) {
		IPAddressesText.push(
			<>
				<IPAddressLink
					key={ header }
					header={ header }
					ipAddress={ ipAddress }
					mapsApiKey={ mapsApiKey }
					hasExtendedSettingsAddOn={ hasExtendedSettingsAddOn }
				/>{ ' ' }
				{ /* Add comma to separate IP addresses, but not after the last one */ }
				{ loopCount < ipAddressesCount - 1 ? ', ' : '' }
			</>
		);

		loopCount++;
	}

	return (
		<EventHeaderItem>
			{ ipAddressesLabel } { IPAddressesText }
		</EventHeaderItem>
	);
}