diff --git a/client/constants.js b/client/constants.js index 565d716fa6..0b4d0fea1a 100644 --- a/client/constants.js +++ b/client/constants.js @@ -1,16 +1,7 @@ // TODO Organize this file by reducer type, to break this apart into // multiple files export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; -export const TOGGLE_SKETCH = 'TOGGLE_SKETCH'; -export const START_SKETCH = 'START_SKETCH'; -export const STOP_SKETCH = 'STOP_SKETCH'; - -export const START_ACCESSIBLE_OUTPUT = 'START_ACCESSIBLE_OUTPUT'; -export const STOP_ACCESSIBLE_OUTPUT = 'STOP_ACCESSIBLE_OUTPUT'; - -export const OPEN_PREFERENCES = 'OPEN_PREFERENCES'; -export const CLOSE_PREFERENCES = 'CLOSE_PREFERENCES'; export const SET_FONT_SIZE = 'SET_FONT_SIZE'; export const SET_LINE_NUMBERS = 'SET_LINE_NUMBERS'; @@ -45,18 +36,11 @@ export const EDIT_COLLECTION = 'EDIT_COLLECTION'; export const DELETE_PROJECT = 'DELETE_PROJECT'; export const SET_SELECTED_FILE = 'SET_SELECTED_FILE'; -export const SHOW_MODAL = 'SHOW_MODAL'; -export const HIDE_MODAL = 'HIDE_MODAL'; export const CREATE_FILE = 'CREATE_FILE'; export const SET_BLOB_URL = 'SET_BLOB_URL'; -export const EXPAND_SIDEBAR = 'EXPAND_SIDEBAR'; -export const COLLAPSE_SIDEBAR = 'COLLAPSE_SIDEBAR'; - export const CONSOLE_EVENT = 'CONSOLE_EVENT'; export const CLEAR_CONSOLE = 'CLEAR_CONSOLE'; -export const EXPAND_CONSOLE = 'EXPAND_CONSOLE'; -export const COLLAPSE_CONSOLE = 'COLLAPSE_CONSOLE'; export const UPDATE_LINT_MESSAGE = 'UPDATE_LINT_MESSAGE'; export const CLEAR_LINT_MESSAGE = 'CLEAR_LINT_MESSAGE'; @@ -75,34 +59,16 @@ export const SET_SOUND_OUTPUT = 'SET_SOUND_OUTPUT'; export const SET_AUTOCLOSE_BRACKETS_QUOTES = 'SET_AUTOCLOSE_BRACKETS_QUOTES'; export const SET_AUTOCOMPLETE_HINTER = 'SET_AUTOCOMPLETE_HINTER'; -export const OPEN_PROJECT_OPTIONS = 'OPEN_PROJECT_OPTIONS'; -export const CLOSE_PROJECT_OPTIONS = 'CLOSE_PROJECT_OPTIONS'; -export const SHOW_NEW_FOLDER_MODAL = 'SHOW_NEW_FOLDER_MODAL'; -export const CLOSE_NEW_FOLDER_MODAL = 'CLOSE_NEW_FOLDER_MODAL'; export const SHOW_FOLDER_CHILDREN = 'SHOW_FOLDER_CHILDREN'; export const HIDE_FOLDER_CHILDREN = 'HIDE_FOLDER_CHILDREN'; -export const OPEN_UPLOAD_FILE_MODAL = 'OPEN_UPLOAD_FILE_MODAL'; -export const CLOSE_UPLOAD_FILE_MODAL = 'CLOSE_UPLOAD_FILE_MODAL'; - -export const SHOW_SHARE_MODAL = 'SHOW_SHARE_MODAL'; -export const CLOSE_SHARE_MODAL = 'CLOSE_SHARE_MODAL'; -export const SHOW_EDITOR_OPTIONS = 'SHOW_EDITOR_OPTIONS'; -export const CLOSE_EDITOR_OPTIONS = 'CLOSE_EDITOR_OPTIONS'; -export const SHOW_KEYBOARD_SHORTCUT_MODAL = 'SHOW_KEYBOARD_SHORTCUT_MODAL'; -export const CLOSE_KEYBOARD_SHORTCUT_MODAL = 'CLOSE_KEYBOARD_SHORTCUT_MODAL'; + export const SHOW_TOAST = 'SHOW_TOAST'; export const HIDE_TOAST = 'HIDE_TOAST'; export const SET_TOAST_TEXT = 'SET_TOAST_TEXT'; export const SET_THEME = 'SET_THEME'; export const SET_LANGUAGE = 'SET_LANGUAGE'; -export const SET_UNSAVED_CHANGES = 'SET_UNSAVED_CHANGES'; export const SET_AUTOREFRESH = 'SET_AUTOREFRESH'; -export const START_SKETCH_REFRESH = 'START_SKETCH_REFRESH'; -export const END_SKETCH_REFRESH = 'END_SKETCH_REFRESH'; - -export const DETECT_INFINITE_LOOPS = 'DETECT_INFINITE_LOOPS'; -export const RESET_INFINITE_LOOPS = 'RESET_INFINITE_LOOPS'; export const RESET_PASSWORD_INITIATE = 'RESET_PASSWORD_INITIATE'; export const RESET_PASSWORD_RESET = 'RESET_PASSWORD_RESET'; @@ -116,20 +82,11 @@ export const EMAIL_VERIFICATION_INVALID = 'EMAIL_VERIFICATION_INVALID'; // eventually, handle errors more specifically and better export const ERROR = 'ERROR'; -export const JUST_OPENED_PROJECT = 'JUST_OPENED_PROJECT'; -export const RESET_JUST_OPENED_PROJECT = 'RESET_JUST_OPENED_PROJECT'; - export const SET_PROJECT_SAVED_TIME = 'SET_PROJECT_SAVED_TIME'; -export const RESET_PROJECT_SAVED_TIME = 'RESET_PROJECT_SAVED_TIME'; -export const SET_PREVIOUS_PATH = 'SET_PREVIOUS_PATH'; -export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL'; -export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL'; export const PERSIST_STATE = 'PERSIST_STATE'; export const CLEAR_PERSISTED_STATE = 'CLEAR_PERSISTED_STATE'; -export const HIDE_RUNTIME_ERROR_WARNING = 'HIDE_RUNTIME_ERROR_WARNING'; -export const SHOW_RUNTIME_ERROR_WARNING = 'SHOW_RUNTIME_ERROR_WARNING'; export const SET_ASSETS = 'SET_ASSETS'; export const DELETE_ASSET = 'DELETE_ASSET'; diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 80a43443bc..8dc6ef6685 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -2,49 +2,48 @@ import * as ActionTypes from '../../../constants'; import { clearConsole } from './console'; import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher'; -export function startVisualSketch() { - return { - type: ActionTypes.START_SKETCH - }; -} - -export function stopVisualSketch() { - return { - type: ActionTypes.STOP_SKETCH - }; -} - -export function startRefreshSketch() { - return { - type: ActionTypes.START_SKETCH_REFRESH - }; -} - -export function startSketchAndRefresh() { - return (dispatch) => { - dispatch(startVisualSketch()); - dispatch(startRefreshSketch()); - }; -} - -export function endSketchRefresh() { - return { - type: ActionTypes.END_SKETCH_REFRESH - }; -} - -export function startAccessibleOutput() { - return { - type: ActionTypes.START_ACCESSIBLE_OUTPUT - }; -} - -export function stopAccessibleOutput() { - return { - type: ActionTypes.STOP_ACCESSIBLE_OUTPUT - }; -} - +import { ideActions } from '../reducers/ide'; + +// TODO: refactor actions which are only used internally by other actions. +const { + startVisualSketch, + stopVisualSketch, + startRefreshSketch, + startAccessibleOutput, + stopAccessibleOutput, + showShareModal: showShareModalInternal +} = ideActions; + +export const { + openUploadFileModal, + closeUploadFileModal, + hideRuntimeErrorWarning, + showRuntimeErrorWarning, + setUnsavedChanges, + endSketchRefresh, // TODO: export is not actually needed + newFile, + closeNewFileModal, + newFolder, + closeNewFolderModal, + expandSidebar, + collapseSidebar, + expandConsole, + collapseConsole, + openPreferences, + closePreferences, + openProjectOptions, + closeProjectOptions, + closeShareModal, + showKeyboardShortcutModal, + closeKeyboardShortcutModal, + showErrorModal, + hideErrorModal, + setPreviousPath, + justOpenedProject, + resetJustOpenedProject +} = ideActions; + +// TODO: move to /files export function setSelectedFile(fileId) { return { type: ActionTypes.SET_SELECTED_FILE, @@ -52,199 +51,27 @@ export function setSelectedFile(fileId) { }; } +// TODO: move to /files export function resetSelectedFile(previousId) { return (dispatch, getState) => { const state = getState(); const newId = state.files.find( (file) => file.name !== 'root' && file.id !== previousId ).id; - dispatch({ - type: ActionTypes.SET_SELECTED_FILE, - selectedFile: newId - }); - }; -} - -export function newFile(parentId) { - return { - type: ActionTypes.SHOW_MODAL, - parentId - }; -} - -export function closeNewFileModal() { - return { - type: ActionTypes.HIDE_MODAL - }; -} - -export function openUploadFileModal(parentId) { - return { - type: ActionTypes.OPEN_UPLOAD_FILE_MODAL, - parentId - }; -} - -export function closeUploadFileModal() { - return { - type: ActionTypes.CLOSE_UPLOAD_FILE_MODAL - }; -} - -export function expandSidebar() { - return { - type: ActionTypes.EXPAND_SIDEBAR - }; -} - -export function collapseSidebar() { - return { - type: ActionTypes.COLLAPSE_SIDEBAR - }; -} - -export function expandConsole() { - return { - type: ActionTypes.EXPAND_CONSOLE - }; -} - -export function collapseConsole() { - return { - type: ActionTypes.COLLAPSE_CONSOLE - }; -} - -export function openPreferences() { - return { - type: ActionTypes.OPEN_PREFERENCES - }; -} - -export function closePreferences() { - return { - type: ActionTypes.CLOSE_PREFERENCES - }; -} - -export function openProjectOptions() { - return { - type: ActionTypes.OPEN_PROJECT_OPTIONS - }; -} - -export function closeProjectOptions() { - return { - type: ActionTypes.CLOSE_PROJECT_OPTIONS - }; -} - -export function newFolder(parentId) { - return { - type: ActionTypes.SHOW_NEW_FOLDER_MODAL, - parentId - }; -} - -export function closeNewFolderModal() { - return { - type: ActionTypes.CLOSE_NEW_FOLDER_MODAL + dispatch(setSelectedFile(newId)); }; } export function showShareModal(projectId, projectName, ownerUsername) { return (dispatch, getState) => { const { project, user } = getState(); - dispatch({ - type: ActionTypes.SHOW_SHARE_MODAL, - payload: { + dispatch( + showShareModalInternal({ shareModalProjectId: projectId || project.id, shareModalProjectName: projectName || project.name, shareModalProjectUsername: ownerUsername || user.username - } - }); - }; -} - -export function closeShareModal() { - return { - type: ActionTypes.CLOSE_SHARE_MODAL - }; -} - -export function showKeyboardShortcutModal() { - return { - type: ActionTypes.SHOW_KEYBOARD_SHORTCUT_MODAL - }; -} - -export function closeKeyboardShortcutModal() { - return { - type: ActionTypes.CLOSE_KEYBOARD_SHORTCUT_MODAL - }; -} - -export function setUnsavedChanges(value) { - return { - type: ActionTypes.SET_UNSAVED_CHANGES, - value - }; -} - -export function detectInfiniteLoops(message) { - return { - type: ActionTypes.DETECT_INFINITE_LOOPS, - message - }; -} - -export function resetInfiniteLoops() { - return { - type: ActionTypes.RESET_INFINITE_LOOPS - }; -} - -export function justOpenedProject() { - return { - type: ActionTypes.JUST_OPENED_PROJECT - }; -} - -export function resetJustOpenedProject() { - return { - type: ActionTypes.RESET_JUST_OPENED_PROJECT - }; -} - -export function setPreviousPath(path) { - return { - type: ActionTypes.SET_PREVIOUS_PATH, - path - }; -} - -export function showErrorModal(modalType) { - return { - type: ActionTypes.SHOW_ERROR_MODAL, - modalType - }; -} - -export function hideErrorModal() { - return { - type: ActionTypes.HIDE_ERROR_MODAL - }; -} - -export function hideRuntimeErrorWarning() { - return { - type: ActionTypes.HIDE_RUNTIME_ERROR_WARNING - }; -} - -export function showRuntimeErrorWarning() { - return { - type: ActionTypes.SHOW_RUNTIME_ERROR_WARNING + }) + ); }; } @@ -269,11 +96,13 @@ export function startSketch() { }; } +// TODO: does this need to call dispatchMessage like in startSketch? Should it call startSketch internally? export function startAccessibleSketch() { return (dispatch) => { dispatch(clearConsole()); dispatch(startAccessibleOutput()); - dispatch(startSketchAndRefresh()); + dispatch(startVisualSketch()); + dispatch(startRefreshSketch()); }; } @@ -287,6 +116,7 @@ export function stopSketch() { }; } +// TODO: move to /files export function createError(error) { return { type: ActionTypes.ERROR, diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 8d45b8b50a..7ef97741b8 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -1,8 +1,11 @@ +import { createSlice } from '@reduxjs/toolkit'; import * as ActionTypes from '../../../constants'; const initialState = { isPlaying: false, + // TODO: this doesn't do anything. isAccessibleOutputPlaying: false, + // TODO: rename ambiguous property modalIsVisible: false, sidebarIsExpanded: false, consoleIsExpanded: true, @@ -10,13 +13,16 @@ const initialState = { projectOptionsVisible: false, newFolderModalVisible: false, uploadFileModalVisible: false, + // TODO: nested properties instead of all at top-level shareModalVisible: false, shareModalProjectId: 'abcd', shareModalProjectName: 'My Cute Sketch', shareModalProjectUsername: 'p5_user', keyboardShortcutVisible: false, unsavedChanges: false, + // TODO: remove dead code, see: PR #849 and issue #698 infiniteLoop: false, + // TODO: this doesn't do anything. previewIsRefreshing: false, infiniteLoopMessage: '', justOpenedProject: false, @@ -26,105 +32,142 @@ const initialState = { parentId: undefined }; -const ide = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.START_SKETCH: - return Object.assign({}, state, { isPlaying: true }); - case ActionTypes.STOP_SKETCH: - return Object.assign({}, state, { isPlaying: false }); - case ActionTypes.START_ACCESSIBLE_OUTPUT: - return Object.assign({}, state, { isAccessibleOutputPlaying: true }); - case ActionTypes.STOP_ACCESSIBLE_OUTPUT: - return Object.assign({}, state, { isAccessibleOutputPlaying: false }); - case ActionTypes.CONSOLE_EVENT: - return Object.assign({}, state, { consoleEvent: action.event }); - case ActionTypes.SHOW_MODAL: - return Object.assign({}, state, { - modalIsVisible: true, - parentId: action.parentId, - newFolderModalVisible: false - }); - case ActionTypes.HIDE_MODAL: - return Object.assign({}, state, { modalIsVisible: false }); - case ActionTypes.COLLAPSE_SIDEBAR: - return Object.assign({}, state, { sidebarIsExpanded: false }); - case ActionTypes.EXPAND_SIDEBAR: - return Object.assign({}, state, { sidebarIsExpanded: true }); - case ActionTypes.COLLAPSE_CONSOLE: - return Object.assign({}, state, { consoleIsExpanded: false }); - case ActionTypes.EXPAND_CONSOLE: - return Object.assign({}, state, { consoleIsExpanded: true }); - case ActionTypes.OPEN_PREFERENCES: - return Object.assign({}, state, { preferencesIsVisible: true }); - case ActionTypes.CLOSE_PREFERENCES: - return Object.assign({}, state, { preferencesIsVisible: false }); - case ActionTypes.RESET_PROJECT: - return initialState; - case ActionTypes.OPEN_PROJECT_OPTIONS: - return Object.assign({}, state, { projectOptionsVisible: true }); - case ActionTypes.CLOSE_PROJECT_OPTIONS: - return Object.assign({}, state, { projectOptionsVisible: false }); - case ActionTypes.SHOW_NEW_FOLDER_MODAL: - return Object.assign({}, state, { - newFolderModalVisible: true, - parentId: action.parentId, - modalIsVisible: false - }); - case ActionTypes.CLOSE_NEW_FOLDER_MODAL: - return Object.assign({}, state, { newFolderModalVisible: false }); - case ActionTypes.SHOW_SHARE_MODAL: - return Object.assign({}, state, { - shareModalVisible: true, - shareModalProjectId: action.payload.shareModalProjectId, - shareModalProjectName: action.payload.shareModalProjectName, - shareModalProjectUsername: action.payload.shareModalProjectUsername - }); - case ActionTypes.CLOSE_SHARE_MODAL: - return Object.assign({}, state, { shareModalVisible: false }); - case ActionTypes.SHOW_KEYBOARD_SHORTCUT_MODAL: - return Object.assign({}, state, { keyboardShortcutVisible: true }); - case ActionTypes.CLOSE_KEYBOARD_SHORTCUT_MODAL: - return Object.assign({}, state, { keyboardShortcutVisible: false }); - case ActionTypes.SET_UNSAVED_CHANGES: - return Object.assign({}, state, { unsavedChanges: action.value }); - case ActionTypes.DETECT_INFINITE_LOOPS: - return Object.assign({}, state, { - infiniteLoop: true, - infiniteLoopMessage: action.message - }); - case ActionTypes.RESET_INFINITE_LOOPS: - return Object.assign({}, state, { - infiniteLoop: false, - infiniteLoopMessage: '' - }); - case ActionTypes.START_SKETCH_REFRESH: - return Object.assign({}, state, { previewIsRefreshing: true }); - case ActionTypes.END_SKETCH_REFRESH: - return Object.assign({}, state, { previewIsRefreshing: false }); - case ActionTypes.JUST_OPENED_PROJECT: - return Object.assign({}, state, { justOpenedProject: true }); - case ActionTypes.RESET_JUST_OPENED_PROJECT: - return Object.assign({}, state, { justOpenedProject: false }); - case ActionTypes.SET_PREVIOUS_PATH: - return Object.assign({}, state, { previousPath: action.path }); - case ActionTypes.SHOW_ERROR_MODAL: - return Object.assign({}, state, { errorType: action.modalType }); - case ActionTypes.HIDE_ERROR_MODAL: - return Object.assign({}, state, { errorType: undefined }); - case ActionTypes.HIDE_RUNTIME_ERROR_WARNING: - return Object.assign({}, state, { runtimeErrorWarningVisible: false }); - case ActionTypes.SHOW_RUNTIME_ERROR_WARNING: - return Object.assign({}, state, { runtimeErrorWarningVisible: true }); - case ActionTypes.OPEN_UPLOAD_FILE_MODAL: - return Object.assign({}, state, { - uploadFileModalVisible: true, - parentId: action.parentId - }); - case ActionTypes.CLOSE_UPLOAD_FILE_MODAL: - return Object.assign({}, state, { uploadFileModalVisible: false }); - default: - return state; - } -}; +const ideSlice = createSlice({ + name: 'ide', + initialState, + reducers: { + startVisualSketch: (state) => { + state.isPlaying = true; + }, + stopVisualSketch: (state) => { + state.isPlaying = false; + }, + startAccessibleOutput: (state) => { + state.isAccessibleOutputPlaying = true; + }, + stopAccessibleOutput: (state) => { + state.isAccessibleOutputPlaying = false; + }, + collapseSidebar: (state) => { + state.sidebarIsExpanded = false; + }, + expandSidebar: (state) => { + state.sidebarIsExpanded = true; + }, + collapseConsole: (state) => { + state.consoleIsExpanded = false; + }, + expandConsole: (state) => { + state.consoleIsExpanded = true; + }, + openPreferences: (state) => { + state.preferencesIsVisible = true; + }, + closePreferences: (state) => { + state.preferencesIsVisible = false; + }, + openProjectOptions: (state) => { + state.projectOptionsVisible = true; + }, + closeProjectOptions: (state) => { + state.projectOptionsVisible = false; + }, + // TODO: rename to openNewFileModal or showNewFileModal + newFile: (state, action) => { + state.modalIsVisible = true; + // TODO: nested properties + state.parentId = action.payload; + state.newFolderModalVisible = false; + }, + closeNewFileModal: (state) => { + state.modalIsVisible = false; + }, + // TODO: rename to openNewFolderModal or showNewFolderModal + newFolder: (state, action) => { + state.newFolderModalVisible = true; + state.parentId = action.payload; + state.modalIsVisible = false; + }, + closeNewFolderModal: (state) => { + state.newFolderModalVisible = false; + }, + openUploadFileModal: (state, action) => { + state.uploadFileModalVisible = true; + state.parentId = action.payload; + }, + closeUploadFileModal: (state) => { + state.uploadFileModalVisible = false; + }, + showShareModal: (state, action) => { + state.shareModalVisible = true; + state.shareModalProjectId = action.payload.shareModalProjectId; + state.shareModalProjectName = action.payload.shareModalProjectName; + state.shareModalProjectUsername = + action.payload.shareModalProjectUsername; + }, + closeShareModal: (state) => { + state.shareModalVisible = false; + }, + showKeyboardShortcutModal: (state) => { + state.keyboardShortcutVisible = true; + }, + closeKeyboardShortcutModal: (state) => { + state.keyboardShortcutVisible = false; + }, + showErrorModal: (state, action) => { + state.errorType = action.payload; + }, + hideErrorModal: (state) => { + state.errorType = undefined; + }, + setUnsavedChanges: (state, action) => { + state.unsavedChanges = action.payload; + }, + detectInfiniteLoops: (state, action) => { + state.infiniteLoop = true; + state.infiniteLoopMessage = action.payload; + }, + resetInfiniteLoops: (state) => { + state.infiniteLoop = false; + state.infiniteLoopMessage = ''; + }, + startRefreshSketch: (state) => { + state.previewIsRefreshing = true; + }, + endSketchRefresh: (state) => { + state.previewIsRefreshing = false; + }, + justOpenedProject: (state) => { + state.justOpenedProject = true; + }, + resetJustOpenedProject: (state) => { + state.justOpenedProject = false; + }, + setPreviousPath: (state, action) => { + state.previousPath = action.payload; + }, + showRuntimeErrorWarning: (state) => { + state.runtimeErrorWarningVisible = true; + }, + hideRuntimeErrorWarning: (state) => { + state.runtimeErrorWarningVisible = false; + } + }, + // Respond to actions which are primarily "owned" by another reducer + extraReducers: (builder) => + builder + .addMatcher( + (action) => action.type === ActionTypes.CONSOLE_EVENT, + (state, action) => { + state.consoleEvent = action.event; + } + ) + .addMatcher( + (action) => action.type === ActionTypes.RESET_PROJECT, + () => initialState + ) +}); + +export const ideActions = ideSlice.actions; -export default ide; +export default ideSlice.reducer;