diff --git a/coediting/hooks/index.js b/coediting/hooks/index.js
new file mode 100644
index 0000000000000..df0c950ade3a0
--- /dev/null
+++ b/coediting/hooks/index.js
@@ -0,0 +1,77 @@
+/**
+ * External dependency
+ */
+import classnames from 'classnames';
+import { connect } from 'react-redux';
+
+/**
+ * WordPress dependency
+ */
+import { BlockEdit, getBlockDefaultClassname, getBlockType, hasBlockSupport } from '@wordpress/blocks';
+import { addFilter } from '@wordpress/hooks';
+import { getWrapperDisplayName } from '@wordpress/element';
+
+/**
+ * Internal dependency
+ */
+import './style.scss';
+// TODO: Move selectors to the local folder
+import {
+ getBlock,
+ getFrozenBlockCollaboratorColor,
+ getFrozenBlockCollaboratorName,
+ isBlockFrozenByCollaborator,
+} from '../../editor/selectors';
+
+const withFrozenMode = ( OriginalComponent, originalProps ) => {
+ const Component = ( { isFrozenByCollaborator, ...props } ) => {
+ if ( isFrozenByCollaborator ) {
+ const { block, collaboratorColor, collaboratorName } = props;
+ const { attributes, name, isValid, uid } = block;
+ const blockType = getBlockType( name );
+
+ // Determine whether the block has props to apply to the wrapper.
+ let wrapperProps;
+ if ( blockType.getEditWrapperProps ) {
+ wrapperProps = blockType.getEditWrapperProps( attributes );
+ }
+
+ // Generate a class name for the block's editable form
+ const generatedClassName = hasBlockSupport( blockType, 'className', true ) ?
+ getBlockDefaultClassname( block.name ) :
+ null;
+ const className = classnames( generatedClassName, block.attributes.className );
+
+ return (
+
+
+
+ { isValid && }
+
+
+ );
+ }
+
+ return ;
+ };
+ Component.displayName = getWrapperDisplayName( OriginalComponent, 'frozen-mode' );
+
+ const mapStateToProps = ( state, { uid } ) => ( {
+ block: getBlock( state, uid ),
+ collaboratorColor: getFrozenBlockCollaboratorColor( state, uid ),
+ collaboratorName: getFrozenBlockCollaboratorName( state, uid ),
+ isFrozenByCollaborator: isBlockFrozenByCollaborator( state, uid ),
+ } );
+
+ return connect( mapStateToProps )( Component );
+};
+
+addFilter( 'Editor.BlockItem', 'coediting/block-item/frozen-mode', withFrozenMode );
diff --git a/coediting/hooks/style.scss b/coediting/hooks/style.scss
new file mode 100644
index 0000000000000..ccfbd03228489
--- /dev/null
+++ b/coediting/hooks/style.scss
@@ -0,0 +1,80 @@
+$coediting-green: #46b450;
+$coediting-orange: #f56e28;
+$coediting-purple: #826eb4;
+$coediting-red: #dc3232;
+$coediting-yellow: #ffb900;
+
+.is-frozen-by-collaborator {
+ &.editor-block-list__block {
+ cursor: not-allowed;
+ pointer-events: none;
+ user-select: none;
+
+ &:before {
+ outline: 1px solid;
+ }
+
+ .coediting-legend {
+ color: $white;
+ font-size: $default-font-size;
+ padding: 2px 5px;
+ position: absolute;
+ right: 0;
+ top: 0;
+ z-index: 50;
+
+ @include break-small {
+ right: $block-mover-padding-visible
+ }
+ }
+
+ &.is-green {
+ &:before {
+ outline-color:$coediting-green;
+ }
+ .coediting-legend {
+ background-color: $coediting-green;
+ }
+ }
+
+ &.is-orange {
+ &:before {
+ outline-color:$coediting-orange;
+ }
+ .coediting-legend {
+ background-color: $coediting-orange;
+ }
+ }
+
+ &.is-purple {
+ &:before {
+ outline-color:$coediting-purple;
+ }
+ .coediting-legend {
+ background-color: $coediting-purple;
+ }
+ }
+
+ &.is-red {
+ &:before {
+ outline-color:$coediting-red;
+ }
+ .coediting-legend {
+ background-color: $coediting-red;
+ }
+ }
+
+ &.is-yellow {
+ &:before {
+ outline-color:$coediting-yellow;
+ }
+ .coediting-legend {
+ background-color: $coediting-yellow;
+ }
+ }
+ }
+
+ .editor-block-list__block-edit {
+ opacity: 0.8;
+ }
+}
diff --git a/coediting/index.js b/coediting/index.js
index ea7ab0df821ca..db12e2903ba68 100644
--- a/coediting/index.js
+++ b/coediting/index.js
@@ -13,6 +13,7 @@ import { camelCaseKeysDeep } from '@wordpress/utils';
/**
* Internal dependencies
*/
+import './hooks';
import Signal from './signal';
export default class Coediting extends EventEmitter {
@@ -37,16 +38,6 @@ export default class Coediting extends EventEmitter {
this.init();
}
- /**
- * Returns random color from a list of arrays.
- *
- * @return {string} color from array.
- */
- static getColor() {
- const colors = [ 'red', 'purple', 'orange', 'yellow', 'green' ];
- return colors[ Math.floor( Math.random() * colors.length ) ];
- }
-
/**
* Generates uuid which is used for url unique hash.
* @return {string} uuid.
@@ -143,9 +134,9 @@ export default class Coediting extends EventEmitter {
this.emit( 'peerData', parsedData );
} );
- this.peer.on( 'close', ( peer ) => {
+ this.peer.on( 'close', () => {
this.isConnected = false;
- this.emit( 'peerClosed', peer );
+ this.emit( 'peerClosed', );
clearInterval( this.listenSignalTimer );
delete this._events.initiator;
delete this._events.peerFound;
diff --git a/editor/actions.js b/editor/actions.js
index 2d7eec8e7cfc2..f01409dc80df2 100644
--- a/editor/actions.js
+++ b/editor/actions.js
@@ -506,7 +506,7 @@ export function toggleFeature( feature ) {
}
export {
- clearFrozenBlock,
+ clearFrozenBlocks,
freezeBlock,
toggleCoediting,
} from './state/coediting';
diff --git a/editor/assets/stylesheets/_variables.scss b/editor/assets/stylesheets/_variables.scss
index ccb136099d3dd..be785e8e28f70 100644
--- a/editor/assets/stylesheets/_variables.scss
+++ b/editor/assets/stylesheets/_variables.scss
@@ -80,13 +80,6 @@ $block-spacing: 4px;
$button-style__radius-roundrect: 4px;
$button-style__radius-round: 50%;
-/* Coediting */
-$coediting-green: #46b450;
-$coediting-orange: #f56e28;
-$coediting-purple: #826eb4;
-$coediting-red: #dc3232;
-$coediting-yellow: #ffb900;
-
/* Media Queries */
/* All media queries currently in WordPress:
diff --git a/editor/components/block-list/block.js b/editor/components/block-list/block.js
index d85551d785a1f..9117bd3212677 100644
--- a/editor/components/block-list/block.js
+++ b/editor/components/block-list/block.js
@@ -47,7 +47,6 @@ import {
getEditedPostAttribute,
getNextBlock,
getPreviousBlock,
- isBlockFrozenByPeer,
isBlockHovered,
isBlockMultiSelected,
isBlockSelected,
@@ -356,8 +355,6 @@ class BlockListBlock extends Component {
'is-selected': showUI,
'is-multi-selected': isMultiSelected,
'is-hovered': isHovered,
- 'is-frozen-by-peer': this.props.isFrozenByPeer,
- 'is-green': this.props.isFrozenByPeer,
} );
const { onMouseLeave, onFocus, onReplace } = this.props;
@@ -455,7 +452,6 @@ const mapStateToProps = ( state, { uid } ) => ( {
order: getBlockIndex( state, uid ),
meta: getEditedPostAttribute( state, 'meta' ),
mode: getBlockMode( state, uid ),
- isFrozenByPeer: isBlockFrozenByPeer( state, uid ),
} );
const mapDispatchToProps = ( dispatch, ownProps ) => ( {
diff --git a/editor/components/block-list/style.scss b/editor/components/block-list/style.scss
index cd6039d59db57..b2b9c9754895f 100644
--- a/editor/components/block-list/style.scss
+++ b/editor/components/block-list/style.scss
@@ -81,70 +81,6 @@
background: $blue-medium-highlight;
}
- &.is-frozen-by-peer {
- cursor: not-allowed;
- pointer-events: none;
- opacity: 0.8;
-
- .coediting-legend {
- float: right;
- margin-top: -14px;
- margin-right: -14px;
- color: #fff;
- font-size: 12px;
- }
-
- &.is-green {
- &:before {
- outline: 2px solid $coediting-green;
- transition: 0.2s outline;
- }
- .coediting-legend {
- background: $coediting-green;
- }
- }
-
- &.is-orange {
- &:before {
- outline: 2px solid $coediting-orange;
- transition: 0.2s outline;
- }
- .coediting-legend {
- background: $coediting-orange;
- }
- }
-
- &.is-purple {
- &:before {
- outline: 2px solid $coediting-purple;
- transition: 0.2s outline;
- }
- .coediting-legend {
- background: $coediting-purple;
- }
- }
-
- &.is-red {
- &:before {
- outline: 2px solid $coediting-red;
- transition: 0.2s outline;
- }
- .coediting-legend {
- background: $coediting-red;
- }
- }
-
- &.is-yellow {
- &:before {
- outline: 2px solid $coediting-yellow;
- transition: 0.2s outline;
- }
- .coediting-legend {
- background: $coediting-yellow;
- }
- }
- }
-
// selection style for multiple blocks
&.is-multi-selected *::selection {
background: transparent;
diff --git a/editor/middlewares.js b/editor/middlewares.js
index c23dca715c90f..2bb7394f058a9 100644
--- a/editor/middlewares.js
+++ b/editor/middlewares.js
@@ -6,18 +6,12 @@ import {
includes,
} from 'lodash';
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-
/**
* Internal dependencies
*/
import Coediting from '../coediting';
import {
- clearFrozenBlock,
- createInfoNotice,
+ clearFrozenBlocks,
freezeBlock,
toggleCoediting,
} from './actions';
@@ -28,9 +22,9 @@ export function coeditingMiddleware( { dispatch, getState } ) {
const isPeerConnected = () => app && app.isConnected;
- const getActionPeerId = ( action ) => get( action, 'meta.peerId', null );
+ const getActionCollaboratorId = ( action ) => get( action, 'meta.collaboratorId', null );
- const isPeerAction = ( action ) => getActionPeerId( action ) !== null;
+ const isCoeditingAction = ( action ) => getActionCollaboratorId( action ) !== null;
// Starts the coediting and enables its API.
function startCoedting() {
@@ -44,12 +38,15 @@ export function coeditingMiddleware( { dispatch, getState } ) {
app = new Coediting( coeditingId );
app.on( 'peerData', onPeerData );
- app.on( 'peerConnected', function() {
- dispatch( createInfoNotice( __( 'Collaborator joined!' ) ) );
- } );
- app.on( 'peerClosed', function() {
- dispatch( createInfoNotice( __( 'Collaborator left!' ) ) );
+ app.on( 'peerFound', function( { peerId, userId } ) {
+ // TODO: Convert to action
+ dispatch( {
+ type: 'COEDITING_COLLABORATOR_ADD',
+ collaboratorId: peerId,
+ userId,
+ } );
} );
+ // TODO: Remove collaborators when disconnected
}
const stopCoediting = () => {
@@ -65,12 +62,12 @@ export function coeditingMiddleware( { dispatch, getState } ) {
};
const peerSend = ( action ) => {
- if ( ! isPeerConnected() || isPeerAction( action ) ) {
+ if ( ! isPeerConnected() || isCoeditingAction( action ) ) {
return;
}
const allowedActions = [
- 'COEDITING_CLEAR_FROZEN_BLOCK',
+ 'COEDITING_BLOCKS_UNFREEZE',
'COEDITING_FREEZE_BLOCK',
'EDIT_POST',
'INSERT_BLOCKS',
@@ -88,13 +85,13 @@ export function coeditingMiddleware( { dispatch, getState } ) {
app.send( {
...action,
meta: {
- peerId: app.peerId,
+ collaboratorId: app.peerId,
},
} );
};
function onPeerData( action ) {
- if ( getActionPeerId( action ) !== app.peerId ) {
+ if ( getActionCollaboratorId( action ) !== app.peerId ) {
dispatch( action );
}
}
@@ -122,7 +119,7 @@ export function coeditingMiddleware( { dispatch, getState } ) {
peerSend( freezeBlock( action.uid ) );
break;
case 'CLEAR_SELECTED_BLOCK':
- peerSend( clearFrozenBlock() );
+ peerSend( clearFrozenBlocks() );
break;
default:
peerSend( action );
diff --git a/editor/reducer.js b/editor/reducer.js
index 3514f613a88d6..fc6ae9b7659d9 100644
--- a/editor/reducer.js
+++ b/editor/reducer.js
@@ -385,6 +385,7 @@ export function blockSelection( state = { start: null, end: null, focus: null, i
end: action.uid,
focus: action.config || {},
};
+ // TODO: Trigger UPDATE_FOCUS instead to avoid focus change on collaborator side
case 'INSERT_BLOCKS':
return {
start: action.blocks[ 0 ].uid,
@@ -392,6 +393,7 @@ export function blockSelection( state = { start: null, end: null, focus: null, i
focus: {},
isMultiSelecting: false,
};
+ // TODO: Trigger UPDATE_FOCUS instead to avoid focus change on collaborator side
case 'REPLACE_BLOCKS':
if ( ! action.blocks || ! action.blocks.length || action.uids.indexOf( state.start ) === -1 ) {
return state;
diff --git a/editor/selectors.js b/editor/selectors.js
index 7c9d0f6e1ef8c..3c037e79f7ceb 100644
--- a/editor/selectors.js
+++ b/editor/selectors.js
@@ -1138,6 +1138,8 @@ export function isPublishingPost( state ) {
}
export {
- isBlockFrozenByPeer,
+ getFrozenBlockCollaboratorColor,
+ getFrozenBlockCollaboratorName,
+ isBlockFrozenByCollaborator,
isCoeditingEnabled,
} from './state/coediting';
diff --git a/editor/state/coediting/index.js b/editor/state/coediting/index.js
index f75743d101c95..52efa6748df26 100644
--- a/editor/state/coediting/index.js
+++ b/editor/state/coediting/index.js
@@ -2,75 +2,111 @@
* External dependencies
*/
import { combineReducers } from 'redux';
-import { find, get, omit } from 'lodash';
+import { get, has, omit, omitBy, some } from 'lodash';
-/**
- * Reducer returning coediting enabled state.
- *
- * @param {Boolean} state Current state
- * @param {Object} action Dispatched action
- * @return {Boolean} Updated state
- */
-export const enabled = ( state = false, action ) => {
- switch ( action.type ) {
- case 'COEDITING_TOGGLE':
- return ! state;
- }
- return state;
+const colors = [ 'red', 'purple', 'orange', 'yellow', 'green' ];
+
+const pickColor = () => {
+ return colors[ Math.floor( Math.random() * colors.length ) ];
};
/**
- * Reducer returning coedting state of peers.
+ * Reducer returning frozen blocks state when coediting.
*
* @param {Boolean} state Current state
* @param {Object} action Dispatched action
* @return {Boolean} Updated state
*/
-export const peers = ( state = {}, action ) => {
- const peerId = get( action, 'meta.peerId', null );
+export const blocks = ( state = {}, action ) => {
+ const collaboratorId = get( action, 'meta.collaboratorId', null );
+ const isCurrentCollaborator = value => value === collaboratorId;
- if ( ! peerId ) {
+ if ( ! collaboratorId ) {
return state;
}
switch ( action.type ) {
- case 'COEDITING_CLEAR_FROZEN_BLOCK':
- if ( ! state[ peerId ] ) {
+ case 'COEDITING_BLOCKS_UNFREEZE':
+ if ( ! some( state, isCurrentCollaborator ) ) {
return state;
}
- return omit( state, peerId );
+ return omitBy( state, isCurrentCollaborator );
case 'COEDITING_FREEZE_BLOCK':
- if ( state[ peerId ] === action.uid ) {
+ if ( state[ action.uid ] === collaboratorId ) {
return state;
}
+ return {
+ ...omitBy( state, isCurrentCollaborator ),
+ [ action.uid ]: collaboratorId,
+ };
+ }
+ return state;
+};
+
+/**
+ * Reducer returning collaborators state when coediting.
+ *
+ * @param {Boolean} state Current state
+ * @param {Object} action Dispatched action
+ * @return {Boolean} Updated state
+ */
+export const collaborators = ( state = {}, action ) => {
+ switch ( action.type ) {
+ case 'COEDITING_COLLABORATOR_ADD':
return {
...state,
- [ peerId ]: action.uid,
+ [ action.collaboratorId ]: {
+ color: pickColor(),
+ name: `User: ${ action.userId }`,
+ userId: action.userId,
+ },
};
+ case 'COEDITING_COLLABORATOR_REMOVE':
+ if ( ! state[ action.collaboratorId ] ) {
+ return state;
+ }
+
+ return omit( state, action.collaboratorId );
+ }
+ return state;
+};
+
+/**
+ * Reducer returning coediting enabled state.
+ *
+ * @param {Boolean} state Current state
+ * @param {Object} action Dispatched action
+ * @return {Boolean} Updated state
+ */
+export const enabled = ( state = false, action ) => {
+ switch ( action.type ) {
+ case 'COEDITING_TOGGLE':
+ return ! state;
}
return state;
};
export const reducer = combineReducers( {
+ blocks,
+ collaborators,
enabled,
- peers,
} );
/**
- * Returns an action object used to clear the block freeze edited by peer.
+ * Returns an action object used to clear the freeze for blocks edited by the collaborator.
*
* @return {Object} Action object
*/
-export function clearFrozenBlock() {
+export function clearFrozenBlocks() {
return {
- type: 'COEDITING_CLEAR_FROZEN_BLOCK',
+ type: 'COEDITING_BLOCKS_UNFREEZE',
};
}
/**
- * Returns an action object used to freeze the block edited by peer.
+ * Returns an action object used to freeze the block edited by the collaborator.
*
* @param {String} uid Block unique ID
* @return {Object} Action object
@@ -93,30 +129,56 @@ export function toggleCoediting() {
};
}
+const getFrozenBlockCollaboratorProp = prop =>
+ ( state, uid ) => {
+ const collaboratorId = get( state, [ 'coediting', 'blocks', uid ], null );
+
+ if ( collaboratorId === null ) {
+ return null;
+ }
+
+ return get( state, [ 'coediting', 'collaborators', collaboratorId, prop ], null );
+ };
+
/**
- * Returns true when coediting is enabled, or false otherwise.
+ * Returns a color assigned to the collaborator when block is frozen or null otherwise.
*
* @param {Object} state Global application state
+ * @param {String} uid Block unique ID
*
- * @return {Boolean} Whether coediting is enabled
+ * @return {String|null} Color when block frozen, null otherwise
*/
-export function isCoeditingEnabled( state ) {
- return get( state, 'coediting.enabled', false );
-}
+export const getFrozenBlockCollaboratorColor = getFrozenBlockCollaboratorProp( 'color' );
/**
- * Returns true when block is frozen be peer, or false otherwise.
+ * Returns a name assigned to the collaborator when block is frozen or null otherwise.
*
* @param {Object} state Global application state
* @param {String} uid Block unique ID
*
- * @return {Boolean} Whether block is frozen by peer
+ * @return {String|null} Whether coediting is enabled
*/
-export function isBlockFrozenByPeer( state, uid ) {
- return Boolean(
- find(
- get( state, 'coediting.peers', {} ),
- ( currentUid ) => currentUid === uid,
- )
- );
+export const getFrozenBlockCollaboratorName = getFrozenBlockCollaboratorProp( 'name' );
+
+/**
+ * Returns true when a block is frozen by the collaborator or false otherwise.
+ *
+ * @param {Object} state Global application state
+ * @param {String} uid Block unique ID
+ *
+ * @return {Boolean} Whether block is frozen by collaborator
+ */
+export function isBlockFrozenByCollaborator( state, uid ) {
+ return has( state, [ 'coediting', 'blocks', uid ], false );
+}
+
+/**
+ * Returns true when coediting is enabled, or false otherwise.
+ *
+ * @param {Object} state Global application state
+ *
+ * @return {Boolean} Whether coediting is enabled
+ */
+export function isCoeditingEnabled( state ) {
+ return get( state, 'coediting.enabled', false );
}
diff --git a/editor/state/coediting/test/index.js b/editor/state/coediting/test/index.js
index bf76c344f9ab1..821f8258ebe76 100644
--- a/editor/state/coediting/test/index.js
+++ b/editor/state/coediting/test/index.js
@@ -7,49 +7,24 @@ import deepFreeze from 'deep-freeze';
* Internal dependencies
*/
import {
- clearFrozenBlock,
+ blocks,
+ clearFrozenBlocks,
+ collaborators,
enabled,
freezeBlock,
- isBlockFrozenByPeer,
+ isBlockFrozenByCollaborator,
isCoeditingEnabled,
- peers,
toggleCoediting,
} from '../';
describe( 'coediting state', () => {
- const peerId = 'peer-id';
+ const collaboratorId = 'collaborator-id';
const uid = 'block-uid';
- describe( 'enabled reducer', () => {
- it( 'should update to true when no previous state provided', () => {
- const state = enabled( undefined, {
- type: 'COEDITING_TOGGLE',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should update to true when the previous state was false', () => {
- const state = enabled( false, {
- type: 'COEDITING_TOGGLE',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should update to false when the previous state was true', () => {
- const state = enabled( true, {
- type: 'COEDITING_TOGGLE',
- } );
-
- expect( state ).toBe( false );
- } );
- } );
-
- describe( 'peers reducer', () => {
+ describe( 'blocks reducer', () => {
it( 'should not update the state when no meta provided', () => {
const initialState = deepFreeze( {} );
- const state = peers( initialState, {
+ const state = blocks( initialState, {
type: 'COEDITING_FREEZE_BLOCK',
uid,
} );
@@ -57,74 +32,74 @@ describe( 'coediting state', () => {
expect( state ).toBe( initialState );
} );
- it( 'should add a new block frozen by peer', () => {
+ it( 'should add a new block frozen by collaborator', () => {
const initialState = deepFreeze( {} );
- const state = peers( initialState, {
+ const state = blocks( initialState, {
type: 'COEDITING_FREEZE_BLOCK',
uid,
meta: {
- peerId,
+ collaboratorId,
},
} );
expect( state ).toEqual( {
- [ peerId ]: uid,
+ [ uid ]: collaboratorId,
} );
} );
- it( 'should replace a block frozen by peer', () => {
+ it( 'should replace a block frozen by collaborator', () => {
const initialState = deepFreeze( {
- [ peerId ]: uid,
+ [ uid ]: collaboratorId,
} );
const newUid = 'new-uid';
- const state = peers( initialState, {
+ const state = blocks( initialState, {
type: 'COEDITING_FREEZE_BLOCK',
uid: newUid,
meta: {
- peerId,
+ collaboratorId,
},
} );
expect( state ).toEqual( {
- [ peerId ]: newUid,
+ [ newUid ]: collaboratorId,
} );
} );
- it( 'should not update the state when a block was already frozen by the same peer', () => {
+ it( 'should not update the state when a block was already frozen by the same collaborator', () => {
const initialState = deepFreeze( {
- [ peerId ]: uid,
+ [ uid ]: collaboratorId,
} );
- const state = peers( initialState, {
+ const state = blocks( initialState, {
type: 'COEDITING_FREEZE_BLOCK',
uid,
meta: {
- peerId,
+ collaboratorId,
},
} );
expect( state ).toBe( initialState );
} );
- it( 'should not update the state when no frozen blocks for peer', () => {
+ it( 'should not update the state when no frozen blocks for collaborator', () => {
const initialState = deepFreeze( {} );
- const state = peers( initialState, {
- type: 'COEDITING_CLEAR_FROZEN_BLOCK',
+ const state = blocks( initialState, {
+ type: 'COEDITING_BLOCKS_UNFREEZE',
meta: {
- peerId,
+ collaboratorId,
},
} );
expect( state ).toBe( initialState );
} );
- it( 'should remove the block frozen by peer', () => {
+ it( 'should remove the block frozen by collaborator', () => {
const initialState = deepFreeze( {
- [ peerId ]: uid,
+ [ uid ]: collaboratorId,
} );
- const state = peers( initialState, {
- type: 'COEDITING_CLEAR_FROZEN_BLOCK',
+ const state = blocks( initialState, {
+ type: 'COEDITING_BLOCKS_UNFREEZE',
meta: {
- peerId,
+ collaboratorId,
},
} );
@@ -132,6 +107,86 @@ describe( 'coediting state', () => {
} );
} );
+ describe( 'collaborators reducer', () => {
+ const color = 'green';
+ const userId = 123;
+ it( 'should add new collaborator', () => {
+ const initialState = deepFreeze( {} );
+ const state = collaborators( initialState, {
+ type: 'COEDITING_COLLABORATOR_ADD',
+ collaboratorId,
+ userId,
+ } );
+
+ expect( state ).toEqual( {
+ [ collaboratorId ]: {
+ color: expect.any( String ),
+ name: `User: ${ userId }`,
+ userId,
+ },
+ } );
+ } );
+
+ it( 'should not update the state when trying to remove an unknown collaborator', () => {
+ const initialState = deepFreeze( {
+ [ collaboratorId ]: {
+ color,
+ name: `User: ${ userId }`,
+ userId,
+ },
+ } );
+ const unknownId = 321;
+ const state = collaborators( initialState, {
+ type: 'COEDITING_COLLABORATOR_REMOVE',
+ collaboratorId: unknownId,
+ } );
+
+ expect( state ).toBe( initialState );
+ } );
+
+ it( 'should remove the existing collaborator', () => {
+ const initialState = deepFreeze( {
+ [ collaboratorId ]: {
+ color,
+ name: `User: ${ userId }`,
+ userId,
+ },
+ } );
+ const state = collaborators( initialState, {
+ type: 'COEDITING_COLLABORATOR_REMOVE',
+ collaboratorId,
+ } );
+
+ expect( state ).toEqual( {} );
+ } );
+ } );
+
+ describe( 'enabled reducer', () => {
+ it( 'should update to true when no previous state provided', () => {
+ const state = enabled( undefined, {
+ type: 'COEDITING_TOGGLE',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should update to true when the previous state was false', () => {
+ const state = enabled( false, {
+ type: 'COEDITING_TOGGLE',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should update to false when the previous state was true', () => {
+ const state = enabled( true, {
+ type: 'COEDITING_TOGGLE',
+ } );
+
+ expect( state ).toBe( false );
+ } );
+ } );
+
describe( 'toggleCoediting action', () => {
it( 'should return the COEDITING_TOGGLE action', () => {
expect( toggleCoediting() ).toEqual( {
@@ -149,41 +204,41 @@ describe( 'coediting state', () => {
} );
} );
- describe( 'clearFrozenBlock action', () => {
- it( 'should return the COEDITING_CLEAR_FROZEN_BLOCK action', () => {
- expect( clearFrozenBlock() ).toEqual( {
- type: 'COEDITING_CLEAR_FROZEN_BLOCK',
+ describe( 'clearFrozenBlocks action', () => {
+ it( 'should return the COEDITING_BLOCKS_UNFREEZE action', () => {
+ expect( clearFrozenBlocks() ).toEqual( {
+ type: 'COEDITING_BLOCKS_UNFREEZE',
} );
} );
} );
- describe( 'isBlockFrozenByPeer', () => {
+ describe( 'isBlockFrozenByCollaborator', () => {
const getInitialState = localState => deepFreeze( {
coediting: {
- peers: localState,
+ blocks: localState,
},
} );
- it( 'should return false when no peers data', () => {
+ it( 'should return false when no collaborators data', () => {
const state = getInitialState( null );
- expect( isBlockFrozenByPeer( state, uid ) ).toBe( false );
+ expect( isBlockFrozenByCollaborator( state, uid ) ).toBe( false );
} );
- it( 'should return false when block is not frozen by peer', () => {
+ it( 'should return false when block is not frozen by collaborator', () => {
const state = getInitialState( {
- [ peerId ]: uid,
+ [ uid ]: collaboratorId,
} );
- expect( isBlockFrozenByPeer( state, uid ) ).toBe( true );
+ expect( isBlockFrozenByCollaborator( state, uid ) ).toBe( true );
} );
- it( 'should return true when block is frozen by peer', () => {
+ it( 'should return true when block is frozen by collaborator', () => {
const state = getInitialState( {
- [ peerId ]: 'unknown',
+ unknown: collaboratorId,
} );
- expect( isBlockFrozenByPeer( state, uid ) ).toBe( false );
+ expect( isBlockFrozenByCollaborator( state, uid ) ).toBe( false );
} );
} );