/**
* WordPress dependencies
*/
// eslint-disable-next-line import/named
import { BlockInstance, getBlockType } from '@wordpress/blocks';
import {
Button,
Dashicon,
__experimentalDivider as Divider,
KeyboardShortcuts,
Tooltip,
} from '@wordpress/components';
import { select as selectFn, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import {
arrowLeft,
arrowRight,
calendar,
check,
closeSmall,
external,
Icon,
page,
people,
post,
postAuthor,
seen,
} from '@wordpress/icons';
/**
* Internal dependencies
*/
import { GutenbergFunction } from '../../../../@types/gutenberg/types';
import { Thumbnail } from '../../../common/components/thumbnail';
import { getSmartShortDate } from '../../../common/utils/date';
import { formatToImpreciseNumber } from '../../../common/utils/number';
import { InboundSmartLink, SmartLink } from '../provider';
import { BlockPreview } from './component-block-preview';
import { InboundLinkDetails } from './component-inbound-link';
/**
* The props for the SuggestionBreadcrumb component.
*
* @since 3.16.0
*/
type SuggestionBreadcrumbProps = {
link: SmartLink,
};
/**
* Displays the breadcrumb for the suggestion.
*
* It shows the parent blocks of the block where the link is found.
*
* @since 3.16.0
*
* @param {SuggestionBreadcrumbProps} props The component's props.
*/
const SuggestionBreadcrumb = ( { link }: SuggestionBreadcrumbProps ): React.JSX.Element => {
const blockId = link.match?.blockId;
// Fetch block details and parent IDs using the blockId.
const { block, parents } = useSelect(
( select ) => {
const { getBlock, getBlockParents } = select( 'core/block-editor' ) as GutenbergFunction;
if ( ! blockId ) {
return { block: undefined, parents: [] };
}
return {
block: getBlock( blockId ),
parents: getBlockParents( blockId )
.map( ( id ) => getBlock( id ) )
.filter( ( currBlock ): currBlock is BlockInstance => currBlock !== undefined ),
};
},
[ blockId ],
);
if ( ! block ) {
return <>>;
}
return (
{ parents.map( ( parent: BlockInstance, index: number ) => (
{ getBlockType( parent.name )?.title }
/
) ) }
{ getBlockType( block.name )?.title }
{ block.attributes?.metadata?.name && (
{ block.attributes.metadata.name }
) }
);
};
/**
* The LinkDetails component, which renders the details of the link suggestion.
*
* @since 3.16.0
* @since 3.18.0 Added the post type to the link details.
*
* @param {{link: SmartLink}} props The component's props.
*/
const LinkDetails = ( { link }: { link: SmartLink } ): React.JSX.Element => {
const author = link.wp_post_meta?.author ?? __( 'N/A', 'wp-parsely' );
const avgEngaged = link.post_stats?.avg_engaged ?? __( 'N/A', 'wp-parsely' );
const date = link.wp_post_meta?.date ? getSmartShortDate( new Date( link.wp_post_meta.date ) ) : __( 'N/A', 'wp-parsely' );
const thumbnail = link.wp_post_meta?.thumbnail ?? false;
const title = link.wp_post_meta?.title ?? __( 'N/A', 'wp-parsely' );
const type = link.wp_post_meta?.type ?? __( 'External', 'wp-parsely' );
const url = link.wp_post_meta?.url; // Used for the link button.
const views = link.post_stats?.views ? formatToImpreciseNumber( link.post_stats.views ) : __( 'N/A', 'wp-parsely' );
const visitors = link.post_stats?.visitors ? formatToImpreciseNumber( link.post_stats.visitors ) : __( 'N/A', 'wp-parsely' );
return (
{ thumbnail ? (
) : (
) }
{ title }
{ url && (
) }
{ date }
{ author }
{ type }
{ link.post_stats && (
{ views && (
{ views }
) }
{ visitors && (
{ visitors }
) }
{ avgEngaged && (
{ avgEngaged }
) }
) }
);
};
/**
* The props for the ReviewSuggestion component.
*
* @since 3.16.0
*/
type ReviewSuggestionProps = {
link: SmartLink,
onNext: () => void,
onPrevious: () => void,
onAccept: () => void,
onReject: () => void,
onRemove: () => void,
onSelectInEditor: () => void,
hasPrevious: boolean,
hasNext: boolean,
};
/**
* The ReviewSuggestion component, which renders the review suggestion UI.
*
* @since 3.16.0
*
* @param {ReviewSuggestionProps} props The component's props.
*/
export const ReviewSuggestion = ( {
link,
onNext,
onPrevious,
onAccept,
onReject,
onRemove,
onSelectInEditor,
hasPrevious,
hasNext,
}: ReviewSuggestionProps ): React.JSX.Element => {
// If the link is a Inbound Link, render the InboundLinkDetails component.
if ( link && ( link as InboundSmartLink ).post_data !== undefined ) {
return ;
}
if ( ! link?.match ) {
return <>{ __( 'This Smart Link does not have any matches in the current content.', 'wp-parsely' ) }>;
}
const blockId = link.match.blockId;
// Get the block.
const block = selectFn( 'core/block-editor' ).getBlock( blockId );
const isApplied = link.applied;
if ( ! block ) {
return <>{ __( 'No block is selected.', 'wp-parsely' ) }>;
}
return (
{
if ( link && ! link.applied ) {
onAccept();
}
},
r: () => {
if ( ! link ) {
return;
}
if ( link.applied ) {
onRemove();
} else {
onReject();
}
},
} }
/>
{ ! isApplied && (
<>
>
) }
{ isApplied && (
<>
>
) }
);
};