diff --git a/src/components/collaboration-mode-picker.tsx b/src/components/collaboration-mode-picker.tsx index c66653bd..fe8d2122 100644 --- a/src/components/collaboration-mode-picker.tsx +++ b/src/components/collaboration-mode-picker.tsx @@ -1,20 +1,18 @@ import { Button, Popover, Icon } from '@wordpress/components'; +import { + store as coreDataStore, + type CoreDataActions, + type CoreDataSelectors, +} from '@wordpress/core-data'; import { useSelect, useDispatch } from '@wordpress/data'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { pencil, seen } from '@wordpress/icons'; - -import { - store as collaborationModeStore, - type CollaborationModeStoreSelectors, - type CollaborationModeStoreActions, - CollaborationMode, -} from '@/store/collaboration-mode-store'; +import { pencil, seen, chevronDown } from '@wordpress/icons'; import '@/components/collaboration-mode-picker.scss'; interface ModeOption { - value: CollaborationMode; + value: 'edit' | 'view'; label: string; description: string; icon: JSX.Element; @@ -22,13 +20,13 @@ interface ModeOption { const MODES: ModeOption[] = [ { - value: CollaborationMode.EDIT, + value: 'edit', label: __( 'Editing', 'vip-real-time-collaboration' ), description: __( 'Edit document directly', 'vip-real-time-collaboration' ), icon: pencil, }, { - value: CollaborationMode.VIEW, + value: 'view', label: __( 'Viewing', 'vip-real-time-collaboration' ), description: __( 'Focus on content', 'vip-real-time-collaboration' ), icon: seen, @@ -43,17 +41,16 @@ export function CollaborationModePicker() { const [ isPopoverVisible, setIsPopoverVisible ] = useState( false ); const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >( null ); - // Default value is Edit mode - const selectedMode = useSelect< CollaborationModeStoreSelectors, CollaborationMode >( select => - select( collaborationModeStore ).getMode() + const selectedCollaborationEditorMode = useSelect< CoreDataSelectors, 'edit' | 'view' >( select => + select( coreDataStore ).getCollaboratorMode() ); - const { setMode } = useDispatch< CollaborationModeStoreActions >( collaborationModeStore ); + const { setCollaboratorMode } = useDispatch< CoreDataActions >( coreDataStore ); - const currentMode = MODES.find( mode => mode.value === selectedMode ); + const currentMode = MODES.find( mode => mode.value === selectedCollaborationEditorMode ); - const handleModeSelect = ( mode: CollaborationMode ) => { - setMode( mode ); + const handleModeSelect = ( mode: 'edit' | 'view' ) => { + setCollaboratorMode( mode ); setIsPopoverVisible( false ); }; @@ -68,7 +65,9 @@ export function CollaborationModePicker() { ref={ setPopoverAnchor } text={ currentMode?.label } icon={ currentMode?.icon } - /> + > + + { isPopoverVisible && ( { MODES.map( ( { value, label, description, icon } ) => { - const isSelected = value === selectedMode; + const isSelected = value === selectedCollaborationEditorMode; return ( ( new CursorRegistry() ); - // Manage read-only state for the code editor. - useModifyCodeEditor(); + useDisableSidebarInteraction(); return ( <> @@ -128,14 +127,6 @@ export function RTCSettingsPanel() { checked={ isDebugToolsEnabled } onChange={ ( enabled: boolean ) => setSetting( Setting.DEBUG_TOOLS, enabled ) } /> - - - setSetting( Setting.COLLABORATION_MODE_PICKER, enabled ) - } - /> > ) } diff --git a/src/hooks/use-disable-block-editing.ts b/src/hooks/use-disable-block-editing.ts index 048d1888..3aa87ce2 100644 --- a/src/hooks/use-disable-block-editing.ts +++ b/src/hooks/use-disable-block-editing.ts @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useBlockEditingMode } from '@wordpress/block-editor'; +import { store as coreDataStore, type CoreDataSelectors } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; import { addFilter, removeFilter } from '@wordpress/hooks'; @@ -9,11 +10,6 @@ import { addFilter, removeFilter } from '@wordpress/hooks'; /** * Internal dependencies */ -import { - type CollaborationModeStoreSelectors, - store as CollaborationModeStore, - CollaborationMode, -} from '@/store/collaboration-mode-store'; import { Setting, store as rtcSettingsStore, @@ -35,14 +31,13 @@ export function useDisableBlockEditing() { ); // Get the current collaboration mode (view or edit). - const collaborationMode = useSelect< CollaborationModeStoreSelectors, CollaborationMode >( - select => select( CollaborationModeStore ).getMode() + const currentCollaborationEditorMode = useSelect< CoreDataSelectors, 'view' | 'edit' >( select => + select( coreDataStore ).getCollaboratorMode() ); // Determine if view-only mode should be active. // TODO: Remove the Collaboration mode enabled check once we complete the feature. - const isViewOnlyActive = - isCollaborationModeEnabled && collaborationMode === CollaborationMode.VIEW; + const isViewOnlyActive = isCollaborationModeEnabled && currentCollaborationEditorMode === 'view'; // Set the block editing mode based on whether view-only is active. // This hook must be called unconditionally to follow the Rules of Hooks. diff --git a/src/hooks/use-disable-sidebar-interaction.ts b/src/hooks/use-disable-sidebar-interaction.ts new file mode 100644 index 00000000..d289b344 --- /dev/null +++ b/src/hooks/use-disable-sidebar-interaction.ts @@ -0,0 +1,48 @@ +/** + * WordPress dependencies + */ +import { store as coreDataStore, type CoreDataSelectors } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + Setting, + store as rtcSettingsStore, + type SettingsStoreSelectors, +} from '@/store/settings-store'; + +/** + * Custom hook that disables sidebar interaction when in view mode. + */ +export function useDisableSidebarInteraction() { + // Get the current collaboration mode (view or edit). + const currentCollaborationEditorMode = useSelect< CoreDataSelectors, 'view' | 'edit' >( select => + select( coreDataStore ).getCollaboratorMode() + ); + + // Check if the Collaboration Mode Picker setting is enabled. + // TODO: Delete this once we complete the feature. + const isCollaborationModeEnabled = useSelect< SettingsStoreSelectors, boolean >( select => + select( rtcSettingsStore ).getSetting( Setting.COLLABORATION_MODE_PICKER ) + ); + + const shouldSidebarBeReadOnly = + isCollaborationModeEnabled && currentCollaborationEditorMode === 'view'; + + // Manage sidebar interaction. + useEffect( () => { + const sidebarElement = document.querySelector( '.editor-sidebar' ); + const headerSettingsElement = document.querySelector( '.editor-header__settings' ); + + if ( + sidebarElement instanceof HTMLDivElement && + headerSettingsElement instanceof HTMLDivElement + ) { + sidebarElement.style.pointerEvents = shouldSidebarBeReadOnly ? 'none' : 'auto'; + headerSettingsElement.style.pointerEvents = shouldSidebarBeReadOnly ? 'none' : 'auto'; + } + }, [ shouldSidebarBeReadOnly ] ); +} diff --git a/src/hooks/use-modify-code-editor.ts b/src/hooks/use-modify-code-editor.ts deleted file mode 100644 index b0d38734..00000000 --- a/src/hooks/use-modify-code-editor.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { store as editPostStore, type EditPostStoreSelectors } from '@wordpress/edit-post'; -import { useEffect } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { - type CollaborationModeStoreSelectors, - store as CollaborationModeStore, - CollaborationMode, -} from '@/store/collaboration-mode-store'; -import { - Setting, - store as rtcSettingsStore, - type SettingsStoreSelectors, -} from '@/store/settings-store'; - -/** - * Custom hook that manages read-only state for the code editor. - * Makes the code editor textarea read-only when in view mode. - */ -export function useModifyCodeEditor() { - // Get the current editor mode (visual or text). - // Visual mode is the default block editor mode. - // Text mode is the code editor mode. - const editorMode = useSelect< EditPostStoreSelectors, 'visual' | 'text' | undefined >( select => - select( editPostStore ).getEditorMode() - ); - - // Check if the Collaboration Mode Picker setting is enabled. - // TODO: Delete this once we complete the feature. - const isCollaborationModeEnabled = useSelect< SettingsStoreSelectors, boolean >( select => - select( rtcSettingsStore ).getSetting( Setting.COLLABORATION_MODE_PICKER ) - ); - - // Get the current collaboration mode (view or edit). - const collaborationMode = useSelect< CollaborationModeStoreSelectors, CollaborationMode >( - select => select( CollaborationModeStore ).getMode() - ); - - // The code editor is set to read-only in the following cases: - // 1. When the editor is in text mode. - // 2. When the Collaboration Mode Picker is enabled and the current mode is View. - // TODO: Remove the Collaboration mode enabled check once we complete the feature. - const shouldCodeEditorBeReadOnly = - editorMode === 'text' && - ( ! isCollaborationModeEnabled || collaborationMode === CollaborationMode.VIEW ); - - // Manage code editor read-only state. - useEffect( () => { - const editorPostTextEditorElement = document.querySelector( '.editor-post-text-editor' ); - const editorTitleTextEditorElement = document.querySelector( - '.components-textarea-control__input' - ); - - // Set or remove the readOnly attribute on the code editor textarea based on the current mode. - if ( - editorPostTextEditorElement instanceof HTMLTextAreaElement && - editorTitleTextEditorElement instanceof HTMLTextAreaElement - ) { - editorPostTextEditorElement.readOnly = shouldCodeEditorBeReadOnly; - editorTitleTextEditorElement.readOnly = shouldCodeEditorBeReadOnly; - } - }, [ shouldCodeEditorBeReadOnly ] ); -} diff --git a/src/store/collaboration-mode-store.ts b/src/store/collaboration-mode-store.ts deleted file mode 100644 index 10352a1b..00000000 --- a/src/store/collaboration-mode-store.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { register, createReduxStore, StoreDescriptor } from '@wordpress/data'; - -const STORE_NAME = 'vip-real-time-collaboration/collaboration-mode'; - -/** - * Collaboration mode types for the editor. - * - VIEW: Read-only mode, user can view content but not make edits - * - EDIT: Full editing mode, user can make changes to the content - */ -export enum CollaborationMode { - VIEW = 'view', - EDIT = 'edit', -} - -interface CollaborationModeState { - currentMode: CollaborationMode; -} - -const DEFAULT_STATE: CollaborationModeState = { - currentMode: CollaborationMode.EDIT, -}; - -const actions = { - setMode: ( mode: CollaborationMode ): CollaborationModeAction => ( { - type: 'SET_MODE', - payload: { mode }, - } ), -}; - -const reducer = ( - state = DEFAULT_STATE, - action: CollaborationModeAction -): CollaborationModeState => { - switch ( action.type ) { - case 'SET_MODE': { - return { - ...state, - currentMode: action.payload.mode, - }; - } - default: - return state; - } -}; - -const selectors = { - getMode( state: CollaborationModeState ): CollaborationMode { - return state.currentMode; - }, -}; - -type CollaborationModeAction = { - type: 'SET_MODE'; - payload: { - mode: CollaborationMode; - }; -}; - -export const store = createReduxStore( STORE_NAME, { - reducer, - actions, - selectors, -} ); - -( register as ( store: StoreDescriptor ) => void )( store ); - -export type { CollaborationModeState }; - -export type CollaborationModeStoreActions = { - setMode: ( mode: CollaborationMode ) => void; -}; - -export type CollaborationModeStoreSelectors = { - getMode: () => CollaborationMode; -}; diff --git a/src/store/settings-store.ts b/src/store/settings-store.ts index c30aab06..5839a7eb 100644 --- a/src/store/settings-store.ts +++ b/src/store/settings-store.ts @@ -25,6 +25,7 @@ interface SettingsState { [ Setting.POST_UPDATE_NOTIFICATION ]: boolean; [ Setting.USER_ENTER_NOTIFICATION ]: boolean; [ Setting.USER_EXIT_NOTIFICATION ]: boolean; + // ToDo: Delete this once we complete the feature. [ Setting.COLLABORATION_MODE_PICKER ]: boolean; } @@ -36,10 +37,12 @@ const DEFAULT_STATE: SettingsState = { [ Setting.POST_UPDATE_NOTIFICATION ]: true, [ Setting.USER_ENTER_NOTIFICATION ]: true, [ Setting.USER_EXIT_NOTIFICATION ]: false, + // ToDo: Delete this once we complete the feature. [ Setting.COLLABORATION_MODE_PICKER ]: false, }; // Settings that are only available in development mode +// ToDo: Delete COLLABORATION_MODE_PICKER once we complete the feature. const DEV_ONLY_SETTINGS: Setting[] = [ Setting.DEBUG_TOOLS, Setting.COLLABORATION_MODE_PICKER ]; const actions = { @@ -55,6 +58,12 @@ const reducer = ( ): SettingsState => { switch ( action.type ) { case 'SET_SETTING': { + // ToDo: Delete COLLABORATION_MODE_PICKER once we complete the feature. + // Skip saving COLLABORATION_MODE_PICKER as it's computed dynamically + if ( action.payload.setting === Setting.COLLABORATION_MODE_PICKER ) { + return state; + } + const newState = { ...state, [ action.payload.setting ]: action.payload.enabled, @@ -70,10 +79,18 @@ const reducer = ( const selectors = { getSetting( state: SettingsState, setting: Setting ): boolean { - // Special handling for dev-only settings + // ToDo: Delete COLLABORATION_MODE_PICKER once we complete the feature. + // COLLABORATION_MODE_PICKER is always true in development, false otherwise + // It's not stored in state or localStorage + if ( setting === Setting.COLLABORATION_MODE_PICKER ) { + return isDevelopment(); + } + + // Special handling for other dev-only settings if ( DEV_ONLY_SETTINGS.includes( setting ) ) { return isDevelopment() ? state[ setting ] : false; } + return state[ setting ]; }, }; diff --git a/types/wordpress__core-data/index.d.ts b/types/wordpress__core-data/index.d.ts index 8af44bcc..3532f290 100644 --- a/types/wordpress__core-data/index.d.ts +++ b/types/wordpress__core-data/index.d.ts @@ -8,5 +8,9 @@ declare module '@wordpress/core-data' { interface CoreDataSelectors { getCurrentUser(): User; + getCollaboratorMode(): 'view' | 'edit'; + } + interface CoreDataActions { + setCollaboratorMode: ( mode: 'view' | 'edit' ) => void; } }