/** * WordPress dependencies */ import { TextareaControl } from '@safe-wordpress/components'; import { useDispatch, useSelect } from '@safe-wordpress/data'; import { useCallback, useEffect, useMemo, useRef, useState, } from '@safe-wordpress/element'; import { _x } from '@safe-wordpress/i18n'; /** * External dependencies */ import clsx from 'clsx'; import { trim } from 'lodash'; import { store as NC_DATA, useAuthorName, withSubscriptionCheck, } from '@nelio-content/data'; import { DeleteButton } from '@nelio-content/components'; import { dateI18n, getSettings } from '@nelio-content/date'; import { createComment } from '@nelio-content/utils'; import type { EditorialComment, PostId, PostTypeName, UserId, } from '@nelio-content/types'; /** * Internal dependencies */ import { useIsSaving } from '../../hooks'; import { store as NC_POST_EDITOR } from '../../store'; export const Comments = (): JSX.Element => ; const InternalComments = withSubscriptionCheck( 'raw/editorial-comments', () => (
) ); // ======= // HELPERS // ======= const CommentList = (): JSX.Element | null => { const ref = useRef< HTMLUListElement >( null ); const { comments, newComments } = useComments(); const deleteComment = useDeleteComment(); const currentCount = newComments.length; const [ count, setCount ] = useState( -1 ); useEffect( () => { if ( count === currentCount ) { return; } if ( count < currentCount ) { ref.current?.scrollTo( { top: 999_999, behavior: count === -1 ? undefined : 'smooth', } ); } setCount( currentCount ); }, [ count, currentCount, setCount ] ); return ( ); }; const CommentForm = (): JSX.Element => { const [ value, setValue ] = useState( '' ); const isSaving = useIsSaving(); const addComment = useAddComment(); return (
{ if ( 'Enter' !== ev.key ) { return; } ev.preventDefault(); addComment( value ); setValue( '' ); } } />
); }; type CommentProps = { readonly comment: EditorialComment; readonly onDelete?: () => void; }; const Comment = ( { comment, onDelete }: CommentProps ) => { const isSaving = useIsSaving(); const isCurrentUsers = useIsCurrentUsers( comment?.authorId ); const authorName = useAuthorName( comment?.authorId ); return (
  • { comment?.comment || '' }
    { ! onDelete ? (
    { ! isCurrentUsers && (
    { authorName }
    ) }
    { dateI18n( getSettings().formats.datetime, comment?.date ) }
    ) : (
    { isSaving ? _x( 'Saving…', 'text', 'nelio-content' ) : _x( 'New', 'text (editorial comment)', 'nelio-content' ) } { ' • ' }
    ) }
  • ); }; // ===== // HOOKS // ===== const useComments = () => { const postId = useSelect( ( s ) => s( NC_POST_EDITOR ).getId(), [] ); const comments = useSelect( ( s ) => s( NC_DATA ).getCommentsRelatedToPost( postId ), [ postId ] ); const newComments = useSelect( ( s ) => s( NC_POST_EDITOR ).getNewComments(), [] ); return useMemo( () => ( { comments, newComments } ), [ comments, newComments ] ); }; const useDeleteComment = () => { const { setNewComments } = useDispatch( NC_POST_EDITOR ); const newComments = useSelect( ( select ) => select( NC_POST_EDITOR ).getNewComments(), [] ); return useCallback( ( id: EditorialComment[ 'id' ] ) => { void setNewComments( newComments.filter( ( c ) => c.id !== id ) ); }, [ setNewComments, newComments ] ); }; const useAddComment = () => { const { setNewComments } = useDispatch( NC_POST_EDITOR ); const newComments = useSelect( ( select ) => select( NC_POST_EDITOR ).getNewComments(), [] ); return useCallback( ( comment: EditorialComment[ 'comment' ] ) => { const text = trim( comment ); if ( ! text ) { return; } void setNewComments( [ ...newComments, { ...createComment( 0 as PostId, 'post' as PostTypeName ), comment: text, }, ] ); }, [ setNewComments, newComments ] ); }; const useIsCurrentUsers = ( commentAuthor?: UserId ) => useSelect( ( select ) => select( NC_DATA ).getCurrentUserId() === commentAuthor, [ commentAuthor ] );