-
chrisgarrity authored
Fixes the issue where the default project reloads when the locale changes. Why is SET_PROJECT_ID getting dispatched on locale change?
chrisgarrity authoredFixes the issue where the default project reloads when the locale changes. Why is SET_PROJECT_ID getting dispatched on locale change?
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
project-state.js 14.01 KiB
import keyMirror from 'keymirror';
const DONE_CREATING_COPY = 'scratch-gui/project-state/DONE_CREATING_COPY';
const DONE_CREATING_NEW = 'scratch-gui/project-state/DONE_CREATING_NEW';
const DONE_FETCHING_DEFAULT = 'scratch-gui/project-state/DONE_FETCHING_DEFAULT';
const DONE_FETCHING_WITH_ID = 'scratch-gui/project-state/DONE_FETCHING_WITH_ID';
const DONE_LOADING_VM_TO_SAVE = 'scratch-gui/project-state/DONE_LOADING_VM_TO_SAVE';
const DONE_LOADING_VM_WITH_ID = 'scratch-gui/project-state/DONE_LOADING_VM_WITH_ID';
const DONE_LOADING_VM_WITHOUT_ID = 'scratch-gui/project-state/DONE_LOADING_VM_WITHOUT_ID';
const DONE_REMIXING = 'scratch-gui/project-state/DONE_REMIXING';
const DONE_UPDATING = 'scratch-gui/project-state/DONE_UPDATING';
const DONE_UPDATING_BEFORE_NEW = 'scratch-gui/project-state/DONE_UPDATING_BEFORE_NEW';
const SET_PROJECT_ID = 'scratch-gui/project-state/SET_PROJECT_ID';
const START_CREATING_COPY = 'scratch-gui/project-state/START_CREATING_COPY';
const START_CREATING_NEW = 'scratch-gui/project-state/START_CREATING_NEW';
const START_ERROR = 'scratch-gui/project-state/START_ERROR';
const START_FETCHING_NEW = 'scratch-gui/project-state/START_FETCHING_NEW';
const START_LOADING_VM_FILE_UPLOAD = 'scratch-gui/project-state/START_LOADING_FILE_UPLOAD';
const START_REMIXING = 'scratch-gui/project-state/START_REMIXING';
const START_UPDATING = 'scratch-gui/project-state/START_UPDATING';
const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW';
const defaultProjectId = '0'; // hardcoded id of default project
const LoadingState = keyMirror({
NOT_LOADED: null,
ERROR: null,
FETCHING_WITH_ID: null,
FETCHING_NEW_DEFAULT: null,
LOADING_VM_WITH_ID: null,
LOADING_VM_FILE_UPLOAD: null,
LOADING_VM_NEW_DEFAULT: null,
REMIXING: null,
SHOWING_WITH_ID: null,
SHOWING_WITHOUT_ID: null,
CREATING_COPY: null,
UPDATING: null,
UPDATING_BEFORE_NEW: null,
CREATING_NEW: null
});
const LoadingStates = Object.keys(LoadingState);
const getIsFetchingWithoutId = loadingState => (
// LOADING_VM_FILE_UPLOAD is an honorary fetch, since there is no fetching step for file uploads
loadingState === LoadingState.LOADING_VM_FILE_UPLOAD ||
loadingState === LoadingState.FETCHING_NEW_DEFAULT
);
const getIsFetchingWithId = loadingState => (
loadingState === LoadingState.FETCHING_WITH_ID ||
loadingState === LoadingState.FETCHING_NEW_DEFAULT
);
const getIsLoadingWithId = loadingState => (
loadingState === LoadingState.LOADING_VM_WITH_ID ||
loadingState === LoadingState.LOADING_VM_NEW_DEFAULT
);
const getIsCreating = loadingState => (
loadingState === LoadingState.CREATING_NEW ||
loadingState === LoadingState.REMIXING ||
loadingState === LoadingState.CREATING_COPY
);
const getIsUpdating = loadingState => (
loadingState === LoadingState.UPDATING ||
loadingState === LoadingState.UPDATING_BEFORE_NEW
);
const getIsShowingProject = loadingState => (
loadingState === LoadingState.SHOWING_WITH_ID ||
loadingState === LoadingState.SHOWING_WITHOUT_ID
);
const getIsShowingWithId = loadingState => (
loadingState === LoadingState.SHOWING_WITH_ID
);
const getIsShowingWithoutId = loadingState => (
loadingState === LoadingState.SHOWING_WITHOUT_ID
);
const getIsError = loadingState => (
loadingState === LoadingState.ERROR
);
const initialState = {
error: null,
projectData: null,
projectId: null,
loadingState: LoadingState.NOT_LOADED
};
const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState;
switch (action.type) {
case DONE_CREATING_NEW:
// We need to set project id since we just created new project on the server.
// No need to load, we should have data already in vm.
if (state.loadingState === LoadingState.CREATING_NEW) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.projectId
});
}
return state;
case DONE_FETCHING_WITH_ID:
if (state.loadingState === LoadingState.FETCHING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.LOADING_VM_WITH_ID,
projectData: action.projectData
});
}
return state;
case DONE_FETCHING_DEFAULT:
if (state.loadingState === LoadingState.FETCHING_NEW_DEFAULT) {
return Object.assign({}, state, {
loadingState: LoadingState.LOADING_VM_NEW_DEFAULT,
projectData: action.projectData
});
}
return state;
case DONE_LOADING_VM_WITHOUT_ID:
if (state.loadingState === LoadingState.LOADING_VM_FILE_UPLOAD ||
state.loadingState === LoadingState.LOADING_VM_NEW_DEFAULT) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITHOUT_ID
});
}
return state;
case DONE_LOADING_VM_WITH_ID:
if (state.loadingState === LoadingState.LOADING_VM_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID
});
}
return state;
case DONE_LOADING_VM_TO_SAVE:
if (state.loadingState === LoadingState.LOADING_VM_FILE_UPLOAD) {
return Object.assign({}, state, {
loadingState: LoadingState.UPDATING
});
}
return state;
case DONE_REMIXING:
// We need to set project id since we just created new project on the server.
// No need to load, we should have data already in vm.
if (state.loadingState === LoadingState.REMIXING) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.projectId
});
}
return state;
case DONE_CREATING_COPY:
// We need to set project id since we just created new project on the server.
// No need to load, we should have data already in vm.
if (state.loadingState === LoadingState.CREATING_COPY) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.projectId
});
}
return state;
case DONE_UPDATING:
if (state.loadingState === LoadingState.UPDATING) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID
});
}
return state;
case DONE_UPDATING_BEFORE_NEW:
if (state.loadingState === LoadingState.UPDATING_BEFORE_NEW) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_NEW_DEFAULT,
projectId: defaultProjectId
});
}
return state;
case SET_PROJECT_ID:
// if the projectId hasn't actually changed do nothing
if (state.projectId === action.projectId) {
return state;
}
// if setting the default project id, specifically fetch that project
if (action.projectId === defaultProjectId) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_NEW_DEFAULT,
projectId: defaultProjectId
});
}
// if we were already showing a project, and a different projectId is set, only fetch that project if
// projectId has changed. This prevents re-fetching projects unnecessarily.
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
if (state.projectId !== action.projectId) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_WITH_ID,
projectId: action.projectId
});
}
} else { // allow any other states to transition to fetching project
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_WITH_ID,
projectId: action.projectId
});
}
return state;
case START_CREATING_NEW:
if (state.loadingState === LoadingState.SHOWING_WITHOUT_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.CREATING_NEW
});
}
return state;
case START_FETCHING_NEW:
if ([
LoadingState.SHOWING_WITH_ID,
LoadingState.SHOWING_WITHOUT_ID
].includes(state.loadingState)) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_NEW_DEFAULT,
projectId: defaultProjectId
});
}
return state;
case START_LOADING_VM_FILE_UPLOAD:
if ([
LoadingState.NOT_LOADED,
LoadingState.SHOWING_WITH_ID,
LoadingState.SHOWING_WITHOUT_ID
].includes(state.loadingState)) {
return Object.assign({}, state, {
loadingState: LoadingState.LOADING_VM_FILE_UPLOAD,
projectId: null // clear any current projectId
});
}
return state;
case START_REMIXING:
// do not set projectId to null, because we nay reference it in creating project
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.REMIXING
});
}
return state;
case START_UPDATING:
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.UPDATING
});
}
return state;
case START_CREATING_COPY:
// do not set projectId to null, because we nay reference it in creating project
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.CREATING_COPY
});
}
return state;
case START_UPDATING_BEFORE_CREATING_NEW:
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.UPDATING_BEFORE_NEW
});
}
return state;
case START_ERROR:
// NOTE: we should introduce handling in components for showing ERROR state
if ([
LoadingState.CREATING_NEW,
LoadingState.FETCHING_NEW_DEFAULT,
LoadingState.FETCHING_WITH_ID,
LoadingState.LOADING_VM_NEW_DEFAULT,
LoadingState.LOADING_VM_WITH_ID,
LoadingState.REMIXING,
LoadingState.CREATING_COPY,
LoadingState.UPDATING_BEFORE_NEW,
LoadingState.UPDATING
].includes(state.loadingState)) {
return Object.assign({}, state, {
loadingState: LoadingState.ERROR,
error: action.error
});
}
return state;
default:
return state;
}
};
const createProject = () => ({
type: START_CREATING_NEW
});
const doneCreatingProject = (id, loadingState) => {
switch (loadingState) {
case LoadingState.CREATING_NEW:
return {
type: DONE_CREATING_NEW,
projectId: id
};
case LoadingState.CREATING_COPY:
return {
type: DONE_CREATING_COPY,
projectId: id
};
case LoadingState.REMIXING:
return {
type: DONE_REMIXING,
projectId: id
};
default:
break;
}
};
const onFetchedProjectData = (projectData, loadingState) => {
switch (loadingState) {
case LoadingState.FETCHING_WITH_ID:
return {
type: DONE_FETCHING_WITH_ID,
projectData: projectData
};
case LoadingState.FETCHING_NEW_DEFAULT:
return {
type: DONE_FETCHING_DEFAULT,
projectData: projectData
};
default:
break;
}
};
const onLoadedProject = (loadingState, canSave) => {
switch (loadingState) {
case LoadingState.LOADING_VM_WITH_ID:
return {
type: DONE_LOADING_VM_WITH_ID
};
case LoadingState.LOADING_VM_FILE_UPLOAD:
if (canSave) {
return {
type: DONE_LOADING_VM_TO_SAVE
};
}
return {
type: DONE_LOADING_VM_WITHOUT_ID
};
case LoadingState.LOADING_VM_NEW_DEFAULT:
return {
type: DONE_LOADING_VM_WITHOUT_ID
};
default:
break;
}
};
const doneUpdatingProject = loadingState => {
switch (loadingState) {
case LoadingState.UPDATING:
return {
type: DONE_UPDATING
};
case LoadingState.UPDATING_BEFORE_NEW:
return {
type: DONE_UPDATING_BEFORE_NEW
};
default:
break;
}
};
const projectError = error => ({
type: START_ERROR,
error: error
});
const setProjectId = id => ({
type: SET_PROJECT_ID,
projectId: id
});
const requestNewProject = canCreateNew => {
if (canCreateNew) return {type: START_UPDATING_BEFORE_CREATING_NEW};
return {type: START_FETCHING_NEW};
};
const onProjectUploadStarted = () => ({
type: START_LOADING_VM_FILE_UPLOAD
});
const updateProject = () => ({
type: START_UPDATING
});
const saveProjectAsCopy = () => ({
type: START_CREATING_COPY
});
const remixProject = () => ({
type: START_REMIXING
});
export {
reducer as default,
initialState as projectStateInitialState,
LoadingState,
LoadingStates,
createProject,
defaultProjectId,
doneCreatingProject,
doneUpdatingProject,
getIsCreating,
getIsError,
getIsFetchingWithId,
getIsFetchingWithoutId,
getIsLoadingWithId,
getIsShowingProject,
getIsShowingWithId,
getIsShowingWithoutId,
getIsUpdating,
onFetchedProjectData,
onLoadedProject,
onProjectUploadStarted,
projectError,
remixProject,
requestNewProject,
saveProjectAsCopy,
setProjectId,
updateProject
};