UNPKG

@wordpress/editor

Version:
8 lines (7 loc) 11.5 kB
{ "version": 3, "sources": ["../../../src/components/sync-connection-error-modal/index.tsx"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useSelect, select } from '@wordpress/data';\nimport { useCopyToClipboard } from '@wordpress/compose';\n// @ts-ignore No exported types.\nimport { serialize } from '@wordpress/blocks';\nimport {\n\tstore as coreDataStore,\n\tprivateApis as coreDataPrivateApis,\n\ttype ConnectionError,\n} from '@wordpress/core-data';\n// @ts-expect-error - No type declarations available for @wordpress/block-editor\n// prettier-ignore\nimport { privateApis, store as blockEditorStore } from '@wordpress/block-editor';\nimport {\n\tButton,\n\tModal,\n\twithFilters,\n\t__experimentalHStack as HStack,\n\t__experimentalVStack as VStack,\n} from '@wordpress/components';\nimport { useState, useEffect } from '@wordpress/element';\nimport { __, sprintf, _n } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport { getSyncErrorMessages } from '../../utils/sync-error-messages';\nimport { store as editorStore } from '../../store';\nimport { unlock } from '../../lock-unlock';\nimport { useRetryCountdown } from './use-retry-countdown';\n\nconst { BlockCanvasCover } = unlock( privateApis );\nconst { retrySyncConnection } = unlock( coreDataPrivateApis );\n\n// Debounce time for initial disconnected status to allow connection to establish.\nconst INITIAL_DISCONNECTED_DEBOUNCE_MS = 20000;\n\n// Debounce time for showing the disconnect dialog after the intial connection,\n// allowing brief network interruptions to resolve.\nconst DISCONNECTED_DEBOUNCE_MS = 8000;\n\nexport interface SyncConnectionErrorModalProps {\n\tdescription: string; // Modal description.\n\terror?: ConnectionError; // Error object with a `code` property.\n\tmanualRetry?: () => void; // Callback for when the retry button is clicked.\n\tpostType?: { slug?: string; labels?: { name?: string } } | null; // Current post type object.\n\tsecondsRemainingUntilAutoRetry?: number; // Seconds remaining until the next automatic retry attempt, if applicable.\n\ttitle: string; // Modal title.\n}\n\n/**\n * Default sync connection modal component.\n *\n * Can be replaced or wrapped via the `editor.SyncConnectionErrorModal` filter.\n *\n * @param props - SyncConnectionErrorModalProps.\n */\nfunction DefaultSyncConnectionErrorModal(\n\tprops: SyncConnectionErrorModalProps\n) {\n\tconst {\n\t\tdescription,\n\t\tmanualRetry,\n\t\tpostType,\n\t\tsecondsRemainingUntilAutoRetry,\n\t\ttitle,\n\t} = props;\n\tconst copyButtonRef = useCopyToClipboard( () => {\n\t\tconst blocks = select( blockEditorStore ).getBlocks();\n\t\treturn serialize( blocks );\n\t} );\n\n\tlet retryCountdownText: string = '';\n\tlet isRetrying = false;\n\tif (\n\t\tsecondsRemainingUntilAutoRetry &&\n\t\tsecondsRemainingUntilAutoRetry > 0\n\t) {\n\t\tretryCountdownText = sprintf(\n\t\t\t/* translators: %d: number of seconds until retry */\n\t\t\t_n(\n\t\t\t\t'Retrying connection in %d second\\u2026',\n\t\t\t\t'Retrying connection in %d seconds\\u2026',\n\t\t\t\tsecondsRemainingUntilAutoRetry\n\t\t\t),\n\t\t\tsecondsRemainingUntilAutoRetry\n\t\t);\n\t} else if ( 0 === secondsRemainingUntilAutoRetry ) {\n\t\tisRetrying = true;\n\t\tretryCountdownText = __( 'Retrying\\u2026' );\n\t}\n\n\tlet editPostHref = 'edit.php';\n\tif ( postType?.slug ) {\n\t\teditPostHref = `edit.php?post_type=${ postType.slug }`;\n\t}\n\n\treturn (\n\t\t<Modal\n\t\t\toverlayClassName=\"editor-sync-connection-error-modal\"\n\t\t\tisDismissible={ false }\n\t\t\tonRequestClose={ () => {} }\n\t\t\tshouldCloseOnClickOutside={ false }\n\t\t\tshouldCloseOnEsc={ false }\n\t\t\tsize=\"medium\"\n\t\t\ttitle={ title }\n\t\t>\n\t\t\t<VStack spacing={ 6 }>\n\t\t\t\t<p>{ description }</p>\n\t\t\t\t{ retryCountdownText && (\n\t\t\t\t\t<p className=\"editor-sync-connection-error-modal__retry-countdown\">\n\t\t\t\t\t\t{ retryCountdownText }\n\t\t\t\t\t</p>\n\t\t\t\t) }\n\t\t\t\t<HStack justify=\"right\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\thref={ editPostHref }\n\t\t\t\t\t\tisDestructive\n\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{ sprintf(\n\t\t\t\t\t\t\t/* translators: %s: Post type name (e.g., \"Posts\", \"Pages\"). */\n\t\t\t\t\t\t\t__( 'Back to %s' ),\n\t\t\t\t\t\t\tpostType?.labels?.name ?? __( 'Posts' )\n\t\t\t\t\t\t) }\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\tref={ copyButtonRef }\n\t\t\t\t\t\tvariant={ manualRetry ? 'secondary' : 'primary' }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ __( 'Copy Post Content' ) }\n\t\t\t\t\t</Button>\n\t\t\t\t\t{ manualRetry && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t\taccessibleWhenDisabled\n\t\t\t\t\t\t\taria-disabled={ isRetrying }\n\t\t\t\t\t\t\tdisabled={ isRetrying }\n\t\t\t\t\t\t\tisBusy={ isRetrying }\n\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\tonClick={ manualRetry }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{ __( 'Retry' ) }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) }\n\t\t\t\t</HStack>\n\t\t\t</VStack>\n\t\t</Modal>\n\t);\n}\n\n/**\n * Filtered version of the sync connection modal, allowing third-party\n * plugins to replace the default modal via:\n *\n * ```js\n * wp.hooks.addFilter(\n * 'editor.SyncConnectionErrorModal',\n * 'my-plugin/custom-sync-connection-error-modal',\n * ( OriginalComponent ) => ( props ) => {\n * // Return a custom component or wrap the original.\n * return <OriginalComponent { ...props } />;\n * }\n * );\n * ```\n */\n// @ts-ignore\nconst FilteredSyncConnectionErrorModal = globalThis.IS_GUTENBERG_PLUGIN\n\t? withFilters( 'editor.SyncConnectionErrorModal' )(\n\t\t\tDefaultSyncConnectionErrorModal\n\t )\n\t: DefaultSyncConnectionErrorModal;\n\n/**\n * Sync connection modal that displays when any entity reports a disconnection.\n * Uses BlockCanvasCover.Fill to render in the block canvas.\n *\n * @return The modal component or null if not disconnected.\n */\nexport function SyncConnectionErrorModal() {\n\tconst [ hasInitialized, setHasInitialized ] = useState( false );\n\tconst [ showModal, setShowModal ] = useState( false );\n\n\tconst { connectionStatus, isCollaborationEnabled, postType } = useSelect(\n\t\t( selectFn ) => {\n\t\t\tconst currentPostType =\n\t\t\t\tselectFn( editorStore ).getCurrentPostType();\n\t\t\treturn {\n\t\t\t\tconnectionStatus:\n\t\t\t\t\tselectFn( coreDataStore ).getSyncConnectionStatus() || null,\n\t\t\t\tisCollaborationEnabled:\n\t\t\t\t\tselectFn(\n\t\t\t\t\t\teditorStore\n\t\t\t\t\t).isCollaborationEnabledForCurrentPost(),\n\t\t\t\tpostType: currentPostType\n\t\t\t\t\t? selectFn( coreDataStore ).getPostType( currentPostType )\n\t\t\t\t\t: null,\n\t\t\t};\n\t\t},\n\t\t[]\n\t);\n\n\tconst { onManualRetry, secondsRemaining } =\n\t\tuseRetryCountdown( connectionStatus );\n\n\tconst isConnected = 'connected' === connectionStatus?.status;\n\n\t// Set hasInitialized after a debounce to give extra time on initial load.\n\tuseEffect( () => {\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetHasInitialized( true );\n\t\t}, INITIAL_DISCONNECTED_DEBOUNCE_MS );\n\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\tuseEffect( () => {\n\t\tif ( isConnected ) {\n\t\t\tsetShowModal( false );\n\t\t\treturn;\n\t\t}\n\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowModal( true );\n\t\t}, DISCONNECTED_DEBOUNCE_MS );\n\n\t\treturn () => clearTimeout( timeout );\n\t}, [ isConnected ] );\n\n\tif ( ! isCollaborationEnabled || ! hasInitialized || ! showModal ) {\n\t\treturn null;\n\t}\n\n\tconst error =\n\t\tconnectionStatus && 'error' in connectionStatus\n\t\t\t? connectionStatus?.error\n\t\t\t: undefined;\n\tconst manualRetry =\n\t\tconnectionStatus &&\n\t\t'canManuallyRetry' in connectionStatus &&\n\t\tconnectionStatus.canManuallyRetry\n\t\t\t? () => {\n\t\t\t\t\tonManualRetry();\n\t\t\t\t\tretrySyncConnection();\n\t\t\t }\n\t\t\t: undefined;\n\tconst messages = getSyncErrorMessages( error );\n\n\treturn (\n\t\t<BlockCanvasCover.Fill>\n\t\t\t<FilteredSyncConnectionErrorModal\n\t\t\t\tdescription={ messages.description }\n\t\t\t\terror={ error }\n\t\t\t\tmanualRetry={ manualRetry }\n\t\t\t\tpostType={ postType }\n\t\t\t\tsecondsRemainingUntilAutoRetry={ secondsRemaining }\n\t\t\t\ttitle={ messages.title }\n\t\t\t/>\n\t\t</BlockCanvasCover.Fill>\n\t);\n}\n"], "mappings": "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", "names": ["coreDataPrivateApis", "blockEditorStore", "VStack", "HStack", "editorStore", "coreDataStore"] }