Skip to content

Commit

Permalink
Port ExportProject to use redux-toolkit; Remove redux from CreateProj…
Browse files Browse the repository at this point in the history
…ect (#2747)
  • Loading branch information
imnasnainaec authored Oct 30, 2023
1 parent ad96ccc commit 42b599c
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 342 deletions.
5 changes: 0 additions & 5 deletions src/components/App/DefaultState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { defaultState as goalTimelineState } from "components/GoalTimeline/Defau
import { defaultState as loginState } from "components/Login/Redux/LoginReducer";
import { defaultState as currentProjectState } from "components/Project/ProjectReduxTypes";
import { defaultState as exportProjectState } from "components/ProjectExport/Redux/ExportProjectReduxTypes";
import { defaultState as createProjectState } from "components/ProjectScreen/CreateProject/Redux/CreateProjectReduxTypes";
import { defaultState as pronunciationsState } from "components/Pronunciations/Redux/PronunciationsReduxTypes";
import { defaultState as treeViewState } from "components/TreeView/Redux/TreeViewReduxTypes";
import { defaultState as characterInventoryState } from "goals/CharacterInventory/Redux/CharacterInventoryReducer";
Expand All @@ -15,10 +14,6 @@ export const defaultState = {
loginState: { ...loginState },

//project
createProjectState: {
...createProjectState,
success: true,
},
currentProjectState: { ...currentProjectState },
exportProjectState: { ...exportProjectState },

Expand Down
63 changes: 32 additions & 31 deletions src/components/ProjectExport/Redux/ExportProjectActions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import { Action, PayloadAction } from "@reduxjs/toolkit";

import { deleteLift, downloadLift, exportLift } from "backend";
import {
ExportProjectAction,
ExportStatus,
} from "components/ProjectExport/Redux/ExportProjectReduxTypes";
downloadingAction,
exportingAction,
failureAction,
resetAction,
successAction,
} from "components/ProjectExport/Redux//ExportProjectReducer";
import { StoreStateDispatch } from "types/Redux/actions";

// Action Creation Functions

export function exporting(projectId: string): PayloadAction {
return exportingAction(projectId);
}

export function downloading(projectId: string): PayloadAction {
return downloadingAction(projectId);
}

export function failure(projectId: string): PayloadAction {
return failureAction(projectId);
}

export function reset(): Action {
return resetAction();
}

export function success(projectId: string): PayloadAction {
return successAction(projectId);
}

// Dispatch Functions

export function asyncExportProject(projectId: string) {
return async (dispatch: StoreStateDispatch) => {
dispatch(exporting(projectId));
Expand All @@ -27,31 +56,3 @@ export function asyncResetExport() {
await deleteLift();
};
}

function exporting(projectId: string): ExportProjectAction {
return {
type: ExportStatus.Exporting,
projectId,
};
}
function downloading(projectId: string): ExportProjectAction {
return {
type: ExportStatus.Downloading,
projectId,
};
}
export function success(projectId: string): ExportProjectAction {
return {
type: ExportStatus.Success,
projectId,
};
}
export function failure(projectId: string): ExportProjectAction {
return {
type: ExportStatus.Failure,
projectId,
};
}
function reset(): ExportProjectAction {
return { type: ExportStatus.Default };
}
62 changes: 38 additions & 24 deletions src/components/ProjectExport/Redux/ExportProjectReducer.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
import { createSlice } from "@reduxjs/toolkit";

import {
defaultState,
ExportProjectAction,
ExportProjectState,
ExportStatus,
} from "components/ProjectExport/Redux/ExportProjectReduxTypes";
import { StoreAction, StoreActionTypes } from "rootActions";
import { StoreActionTypes } from "rootActions";

const exportProjectSlice = createSlice({
name: "exportProjectState",
initialState: defaultState,
reducers: {
downloadingAction: (state, action) => {
state.projectId = action.payload;
state.status = ExportStatus.Downloading;
},
exportingAction: (state, action) => {
state.projectId = action.payload;
state.status = ExportStatus.Exporting;
},
failureAction: (state, action) => {
state.projectId = action.payload;
state.status = ExportStatus.Failure;
},
resetAction: () => defaultState,
successAction: (state, action) => {
state.projectId = action.payload;
state.status = ExportStatus.Success;
},
},
extraReducers: (builder) =>
builder.addCase(StoreActionTypes.RESET, () => defaultState),
});

export const {
downloadingAction,
exportingAction,
failureAction,
resetAction,
successAction,
} = exportProjectSlice.actions;

export const exportProjectReducer = (
state: ExportProjectState = defaultState,
action: StoreAction | ExportProjectAction
): ExportProjectState => {
switch (action.type) {
case ExportStatus.Exporting:
case ExportStatus.Downloading:
case ExportStatus.Success:
case ExportStatus.Failure:
return {
...defaultState,
projectId: action.projectId ?? "",
status: action.type,
};
case ExportStatus.Default:
case StoreActionTypes.RESET:
return defaultState;
default:
return state;
}
};
export default exportProjectSlice.reducer;
5 changes: 0 additions & 5 deletions src/components/ProjectExport/Redux/ExportProjectReduxTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ export enum ExportStatus {
Failure = "FAILURE",
}

export interface ExportProjectAction {
type: ExportStatus;
projectId?: string;
}

export interface ExportProjectState {
projectId: string;
status: ExportStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { PreloadedState } from "redux";

import { defaultState } from "components/App/DefaultState";
import {
asyncDownloadExport,
asyncExportProject,
asyncResetExport,
} from "components/ProjectExport/Redux/ExportProjectActions";
import { ExportStatus } from "components/ProjectExport/Redux/ExportProjectReduxTypes";
import { RootState, setupStore } from "store";

jest.mock("backend", () => ({
deleteLift: jest.fn,
downloadLift: (...args: any[]) => mockDownloadList(...args),
exportLift: (...args: any[]) => mockExportLift(...args),
}));

const mockDownloadList = jest.fn();
const mockExportLift = jest.fn();
const mockProjId = "project-id";

// Preloaded values for store when testing
const persistedDefaultState: PreloadedState<RootState> = {
...defaultState,
_persist: { version: 1, rehydrated: false },
};

describe("ExportProjectActions", () => {
describe("asyncDownloadExport", () => {
it("correctly affects state on success", async () => {
const store = setupStore();
mockDownloadList.mockResolvedValueOnce({});
await store.dispatch(asyncDownloadExport(mockProjId));
const { projectId, status } = store.getState().exportProjectState;
expect(projectId).toEqual(mockProjId);
expect(status).toEqual(ExportStatus.Downloading);
});

it("correctly affects state on failure", async () => {
const store = setupStore();
mockDownloadList.mockRejectedValueOnce({});
await store.dispatch(asyncDownloadExport(mockProjId));
const { projectId, status } = store.getState().exportProjectState;
expect(projectId).toEqual(mockProjId);
expect(status).toEqual(ExportStatus.Failure);
});
});

describe("asyncExportProject", () => {
it("correctly affects state on success", async () => {
const store = setupStore();
mockExportLift.mockResolvedValueOnce({});
await store.dispatch(asyncExportProject(mockProjId));
const { projectId, status } = store.getState().exportProjectState;
expect(projectId).toEqual(mockProjId);
expect(status).toEqual(ExportStatus.Exporting);
});

it("correctly affects state on failure", async () => {
const store = setupStore();
mockExportLift.mockRejectedValueOnce({});
await store.dispatch(asyncExportProject(mockProjId));
const { projectId, status } = store.getState().exportProjectState;
expect(projectId).toEqual(mockProjId);
expect(status).toEqual(ExportStatus.Failure);
});
});

describe("asyncResetExport", () => {
it("correctly affects state", async () => {
const nonDefaultState = {
projectId: "nonempty-string",
status: ExportStatus.Success,
};
const store = setupStore({
...persistedDefaultState,
exportProjectState: nonDefaultState,
});
await store.dispatch(asyncResetExport());
const { projectId, status } = store.getState().exportProjectState;
expect(projectId).toEqual("");
expect(status).toEqual(ExportStatus.Default);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Typography,
} from "@mui/material";
import { LanguagePicker, languagePickerStrings_en } from "mui-language-picker";
import React, { Fragment, ReactElement, useEffect, useState } from "react";
import React, { Fragment, ReactElement, useState } from "react";
import { Trans, useTranslation } from "react-i18next";

import { WritingSystem } from "api/models";
Expand All @@ -20,10 +20,8 @@ import { FileInputButton, LoadingDoneButton } from "components/Buttons";
import {
asyncCreateProject,
asyncFinishProject,
reset,
} from "components/ProjectScreen/CreateProject/Redux/CreateProjectActions";
import { StoreState } from "types";
import { useAppDispatch, useAppSelector } from "types/hooks";
} from "components/ProjectScreen/CreateProjectActions";
import { useAppDispatch } from "types/hooks";
import theme from "types/theme";
import { newWritingSystem } from "types/writingSystem";

Expand All @@ -41,18 +39,12 @@ const undBcp47 = "und";
export default function CreateProject(): ReactElement {
const dispatch = useAppDispatch();

const { inProgress, success } = useAppSelector(
(state: StoreState) => state.createProjectState
);

useEffect(() => {
dispatch(reset());
}, [dispatch]);

const [analysisLang, setAnalysisLang] = useState(newWritingSystem(undBcp47));
const [error, setError] = useState({ empty: false, nameTaken: false });
const [languageData, setLanguageData] = useState<File | undefined>();
const [loading, setLoading] = useState(false);
const [name, setName] = useState("");
const [success, setSuccess] = useState(false);
const [vernLang, setVernLang] = useState(newWritingSystem(undBcp47));
const [vernLangIsOther, setVernLangIsOther] = useState(false);
const [vernLangOptions, setVernLangOptions] = useState<WritingSystem[]>([]);
Expand Down Expand Up @@ -178,10 +170,16 @@ export default function CreateProject(): ReactElement {
return;
}

setLoading(true);

if (languageData) {
dispatch(asyncFinishProject(name, vernLang));
await dispatch(asyncFinishProject(name, vernLang)).then(() =>
setSuccess(true)
);
} else {
dispatch(asyncCreateProject(name, vernLang, [analysisLang]));
await dispatch(asyncCreateProject(name, vernLang, [analysisLang])).then(
() => setSuccess(true)
);
}
};

Expand Down Expand Up @@ -297,7 +295,7 @@ export default function CreateProject(): ReactElement {
disabled={!vernLang.bcp47 || vernLang.bcp47 === undBcp47}
done={success}
doneText={t("createProject.success")}
loading={inProgress}
loading={loading}
>
{t("createProject.create")}
</LoadingDoneButton>
Expand Down
Loading

0 comments on commit 42b599c

Please sign in to comment.