Skip to content

Commit

Permalink
New version 1.8.1 with plugins blocks compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
webaxones committed Sep 2, 2024
1 parent 44c6729 commit c6c9fe9
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 50 deletions.
2 changes: 1 addition & 1 deletion build/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-plugins', 'wp-rich-text'), 'version' => '525eca4ceb38a5bd7f50');
<?php return array('dependencies' => array('react', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-plugins', 'wp-rich-text'), 'version' => 'ab0245b936622e6790c8');
2 changes: 1 addition & 1 deletion build/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion consistency.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Consistency
* Plugin URI: https://www.webaxones.com
* Description: Fixes typographic and punctuation consistency
* Version: 1.8.0
* Version: 1.8.1
* Requires at least: 6.1
* Requires PHP: 7.4
* Author: Loïc Antignac
Expand Down
2 changes: 1 addition & 1 deletion includes/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Plugin
*/
protected static function setConstants(): void
{
defined( __NAMESPACE__ . '\VERSION' ) || define( __NAMESPACE__ . '\VERSION', '1.8.0' );
defined( __NAMESPACE__ . '\VERSION' ) || define( __NAMESPACE__ . '\VERSION', '1.8.1' );
defined( __NAMESPACE__ . '\PLUGIN_URL' ) || define( __NAMESPACE__ . '\PLUGIN_URL', plugin_dir_url( __DIR__ ) );
defined( __NAMESPACE__ . '\PLUGIN_PATH' ) || define( __NAMESPACE__ . '\PLUGIN_PATH', plugin_dir_path( __DIR__ ) );
defined( __NAMESPACE__ . '\OPTION_GROUP' ) || define( __NAMESPACE__ . '\OPTION_GROUP', 'consistency_plugin' );
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "consistency",
"version": "1.8.0",
"version": "1.8.1",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
6 changes: 5 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Contributors: webaxones
Tags: punctuation, typography, block editor, gutenberg
Requires at least: 6.1
Tested up to: 6.6
Stable tag: 1.8.0
Stable tag: 1.8.1
Requires PHP: 7.4
License: GPL-3.0-or-later
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Expand Down Expand Up @@ -69,6 +69,10 @@ Absolutely not, since nothing is done on the front end. The processing only occu

== Changelog ==

= 1.8.1 =
* Fix: Resolves incompatibilities with block plugins such as Kadence, GeneratePress, etc.
* Fix: Fix the names of enabled or disabled rules in the notices

= 1.8.0 =
* Update: code refactoring (replace global variables with global context, some functions with custom hooks, and allow to process more blocks)

Expand Down
2 changes: 1 addition & 1 deletion src/app/checks.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const shouldProcessBlock = props => {
export const canProcessBlock = currentBlockId => {

const blockAttributes = getBlockAttributes( currentBlockId )

if ( ! blockAttributes || ! blockAttributes.hasOwnProperty( 'content' ) ) return false

if ( isString( blockAttributes.content ) || isRichTextData( blockAttributes.content ) ) {
Expand Down
44 changes: 16 additions & 28 deletions src/app/fixes.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,23 @@ import { updateBlockTextContent } from './data'
import { pairedCharacterSlugs } from '../config/pairedCharacterSlugs'
import { shouldProcessBlock, canProcessBlock, regDealWithPair } from './checks'

const { getBlock, getBlocks, getBlockAttributes, getSelectionStart, isTyping, getBlockSelectionStart } = select( 'core/block-editor' )
const { selectionChange, updateBlockAttributes } = dispatch( 'core/block-editor' )

const { getBlock, getBlocks, getBlockAttributes, getSelectionStart, isTyping } = select( 'core/block-editor' )
const { updateBlockAttributes } = dispatch( 'core/block-editor' )


/**
* Fixes the current block based on the provided rules and attributes.
*
* @param {Object} props - The props object.
* @param {string} props.currentBlockId - The ID of the current block.
* @param {boolean} props.isPasting - Indicates if the content is being pasted.
* @param {boolean} props.isPreviousFixCanceled - Indicates if the previous fix was canceled.
* @param {function} props.setPreviousFixCanceled - The function to set the previous fix canceled state.
* @param {Array} props.blocksToBeProcessed - The blocks to be processed: we pass them as props to avoid using global context since we are not in a component.
* @param {Ref} props.cursorOffsetRef - The reference to the cursor offset.
*/
export const fixIt = props => {

const { currentBlockId, isPasting, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed } = props
const { currentBlockId, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed, cursorOffsetRef } = props

// Check if the current block should be processed and can be processed
if ( ! shouldProcessBlock( { currentBlockId, blocksToBeProcessed } ) || ! canProcessBlock( currentBlockId ) ) return
Expand Down Expand Up @@ -135,42 +134,32 @@ export const fixIt = props => {
if ( ! isPreviousFixCanceled ) {
contentUpdated = updateBlockTextContent( { block, currentBlockId, blockAttributes, blockContent } )
}

// Cursor repositioning:
if ( 0 === cursorPosition || isPasting ) return

// Get the number of characters moved by the replacement: needed for cursor repositioning.
// If the number depends on the replaced string length, we use a function to get it
const nbMoved = typeof reg.nbMoved === 'function' ? reg.nbMoved( lastPart ) : reg.nbMoved || 0

let attributeKey = selectionStart.hasOwnProperty( 'attributeKey' ) ? selectionStart.attributeKey : 'content'

// If the replaced string had more characters than the new string, the cursor has moved forward, so it must be moved back
// Eg: ... replaced with … removes 2 characters
if ( nbMoved < 0 ) {
selectionChange( currentBlockId, attributeKey, cursorPosition + nbMoved, cursorPosition + nbMoved )
}

// If the replaced string had fewer characters than the new string, the cursor has moved backwards, so it must be moved forward
// Eg: "" replaced with « » adds 2 characters
if ( nbMoved > 0 ) {
selectionChange( currentBlockId, attributeKey, cursorPosition + 1 + nbMoved, cursorPosition + nbMoved )
}

if ( 0 === nbMoved ) {
selectionChange( currentBlockId, attributeKey, cursorPosition, cursorPosition )
// Set the cursor offset
if ( contentUpdated ) {
cursorOffsetRef.current = nbMoved
}

} )

}

/**
* Fixes all blocks generated by pasting.
* Fix all blocks generated by pasting based on the provided rules and blocks to be processed.
*
* @param {Object} props - The props object containing necessary data.
* @param {boolean} props.isPreviousFixCanceled - Flag indicating if the previous fix was canceled.
* @param {function} props.setPreviousFixCanceled - Function to set the flag indicating if the previous fix was canceled.
* @param {Array} props.blocksToBeProcessed - Array of block names to be processed.
* @param {Ref} props.cursorOffsetRef - Reference to the cursor offset.
*/
export const fixAll = props => {

const { isPreviousFixCanceled, setPreviousFixCanceled, localizedRuleSettings, blocksToBeProcessed } = props
const { isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed, cursorOffsetRef } = props

// Get the relevant rules
let localizedRules = getLocalizedRules()
Expand Down Expand Up @@ -223,12 +212,11 @@ export const fixAll = props => {
}

// Select all innerBlocks to trigger their correction, then deselect all by selecting the first block
const isPasting = true
allInners.forEach( block => {

if ( ! blocksToBeProcessed.includes( block.name ) ) return
const currentBlockId = block.clientId
block?.clientId && fixIt( { currentBlockId, isPasting, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed } )
block?.clientId && fixIt( { currentBlockId, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed, cursorOffsetRef } )

} )

Expand Down
55 changes: 55 additions & 0 deletions src/app/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* @author Loïc Antignac.
*/

import { select, dispatch } from '@wordpress/data'
const { getBlock, getBlocks, getBlockAttributes, getSelectionStart, isTyping } = select( 'core/block-editor' )
const { selectionChange, updateBlockAttributes } = dispatch( 'core/block-editor' )

/**
* External dependencies
*/
Expand Down Expand Up @@ -79,4 +83,55 @@ export const getLocalizedRules = () => {

return localizedRules

}

/**
* Moves the cursor to right position after fixing the content.
* We use window.getSelection() and not the editor's selectionChange() to avoid to trigger a new state change.
*
* @returns {void}
*/
export const moveCursorToNewPosition = cursorOffset => {

const selection = window.getSelection()

// Stop here if no selection or if the selection anchor node is null
if ( ! selection ) return

// Get current cursor position
let cursorPosition

// Element node case: we need to get the first text node from the element node
if ( selection?.anchorNode?.nodeType === 1 ) {
cursorPosition = selection.anchorNode?.firstChild?.length || 0
}

// Text node case
if ( selection?.anchorNode?.nodeType === 3 ) {
cursorPosition = selection.anchorOffset || 0
}

// If the cursorOffset is positive, newPosition equals cursorPosition + cursorOffset, else newPosition equals cursorPosition
const newPosition = cursorOffset >= 0 ? cursorPosition + cursorOffset : cursorPosition

// Element node
if ( selection?.anchorNode?.nodeType === 1 ) {

// If the anchor node is an element node, we have to get the first text node from it
const textNode = selection.anchorNode?.firstChild || selection.anchorNode?.childNodes[ 0 ]
selection.collapse( textNode, newPosition )
return

}

// Text node
if ( selection?.anchorNode?.nodeType === 3 ) {

// If the anchor node is a text node, we have to get the parent element node
const textNode = selection.anchorNode
selection.collapse( textNode, newPosition )
return

}

}
2 changes: 1 addition & 1 deletion src/components/GlobalSettingPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const GlobalSettingPanel = () => (
<GlobalSettingToggle
key={ key }
settingSlug={ rule.slug }
name={ rule.name }
settingName={ rule.name }
settingDescription={ {
__html: rule.description
} }
Expand Down
8 changes: 8 additions & 0 deletions src/contexts/GlobalContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ export const GlobalProvider = ( { children } ) => {

// This variable is used to check if custom fields are active because the editor content is within an iframe when custom fields are inactive
const [ isEditorInIframe ] = useState( document.querySelector( 'iframe[name="editor-canvas"]' ) !== null )

// This variable is used to avoid new fixes when the user is undoing a fix with CTRL/CMD Z
const [ isPreviousFixCanceled, setPreviousFixCanceled ] = useState( false )

// This variable is used to store the content of the block to avoid fixing it if it has not changed since we check at every state change
const [ previousFixCanceledContent, setPreviousFixCanceledContent ] = useState( '' )

// This variable is used to store the blocks allowed to be processed
const [ blocksToBeProcessed, setBlocksToBeProcessed ] = useState( [] )

// This variable is used to store the number of characters lost or gained by the correction in order to move the cursor accordingly
const cursorOffsetRef = useRef( 0 )

// This ref is used to track if content has been past
const isContentPastedRef = useRef( false )

Expand All @@ -48,6 +55,7 @@ export const GlobalProvider = ( { children } ) => {
setPreviousFixCanceledContent,
blocksToBeProcessed,
setBlocksToBeProcessed,
cursorOffsetRef,
isContentPastedRef
} }>
{ children }
Expand Down
27 changes: 19 additions & 8 deletions src/hooks/useEditorEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { subscribe, select } from '@wordpress/data'
import GlobalContext from '../contexts/GlobalContext'
import { fixIt, fixAll } from '../app/fixes'
import { fetchCurrentUserSettings } from '../app/data'
import { getLocalizedRuleSettings } from '../app/helpers'
import { moveCursorToNewPosition, getLocalizedRuleSettings } from '../app/helpers'

// Get the current selected block and its attributes
const { getSelectedBlockClientId, isTyping, getBlockAttributes } = select( 'core/block-editor' )
Expand All @@ -35,13 +35,15 @@ const useEditorEffects = () => {
previousFixCanceledContent,
setPreviousFixCanceledContent,
blocksToBeProcessed,
cursorOffsetRef,
isContentPastedRef
} = useContext( GlobalContext )

// Initialize the cursorOffset variable if it is undefined
cursorOffsetRef.current = cursorOffsetRef.current || 0

// Initialize the isContentPastedRef variable if it is undefined
if ( isContentPastedRef.current === undefined ) {
isContentPastedRef.current = false
}
isContentPastedRef.current = isContentPastedRef.current || false

useEffect( () => {

Expand All @@ -59,7 +61,7 @@ const useEditorEffects = () => {
// If onPaste setting is enabled and if content has been copied/pasted generating blocks, we fix all blocks then stop here
if ( onPaste && isContentPastedRef.current === true ) {
isContentPastedRef.current = false
fixAll( { isPreviousFixCanceled, setPreviousFixCanceled, localizedRuleSettings, blocksToBeProcessed } )
fixAll( { isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed, cursorOffsetRef } )
return
}

Expand All @@ -81,13 +83,22 @@ const useEditorEffects = () => {
setPreviousFixCanceledContent( blockAttributes.content )

// Fix the current selected block
isTyping() && fixIt( { currentBlockId, isPasting: false, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed } )
isTyping() && fixIt( { currentBlockId, isPreviousFixCanceled, setPreviousFixCanceled, blocksToBeProcessed, cursorOffsetRef } )

} )

// Unsubscribe when the component is unmounted
return () => unsubscribe()
return () => {
unsubscribe()

// Move cursor to the new position
moveCursorToNewPosition( cursorOffsetRef.current )

// Reset cursorOffsetRef
cursorOffsetRef.current = 0
}

}, [ isPreviousFixCanceled, setPreviousFixCanceled, previousFixCanceledContent, setPreviousFixCanceledContent, blocksToBeProcessed, isContentPastedRef ] )
}, [ isPreviousFixCanceled, setPreviousFixCanceled, previousFixCanceledContent, setPreviousFixCanceledContent, blocksToBeProcessed, cursorOffsetRef, isContentPastedRef ] )
}

export default useEditorEffects
9 changes: 3 additions & 6 deletions src/hooks/useSetAllowedBlocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ const useSetAllowedBlocks = () => {
return block.attributes && block.attributes.content && ( block.attributes.content.type === 'string' || block.attributes.content.type === 'rich-text' )
} )

// Remove blocks beginning with kadence/ from the list since it doesn't work with them
richTextAndStringContentTypes = richTextAndStringContentTypes.filter( block => ! block.name.startsWith( 'kadence/' ) )

// Remove blocks from the list cause we don't want to alter them (core/html) or they don't work properly (core/freeform)
const blocksToExclude = [ 'core/html', 'core/freeform' ]
// Remove blocks from the list cause we don't want to alter them (core/html)
const blocksToExclude = [ 'core/html' ]
richTextAndStringContentTypes = richTextAndStringContentTypes.filter( block => ! blocksToExclude.includes( block.name ) )

console.log( 'Consistency - Processed blocks:', richTextAndStringContentTypes );
console.log( 'Consistency - Blocks that can be processed:', richTextAndStringContentTypes );

// Set the blocks to be processed in the global context
setBlocksToBeProcessed( richTextAndStringContentTypes.map( block => block.name ) )
Expand Down

0 comments on commit c6c9fe9

Please sign in to comment.