Skip to content

Commit

Permalink
upgrade react and redux dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
ndepaola committed Oct 23, 2024
1 parent 46a457c commit ed037f2
Show file tree
Hide file tree
Showing 20 changed files with 264 additions and 275 deletions.
306 changes: 130 additions & 176 deletions frontend/package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"generate-schema-types": "npx json2ts -i \"../common/schemas/*.json\" --cwd ../common/schemas > src/common/schema_types.ts"
},
"dependencies": {
"@hello-pangea/dnd": "^16.2.0",
"@hello-pangea/dnd": "^17.0.0",
"@popperjs/core": "^2.11.6",
"@reduxjs/toolkit": "^1.9.5",
"@reduxjs/toolkit": "^2.3.0",
"@types/ua-parser-js": "^0.7.36",
"ajv": "^8.12.0",
"async-await-queue": "^2.1.4",
Expand All @@ -30,13 +30,13 @@
"next": "^13.5.4",
"nextjs-google-analytics": "^2.3.3",
"ping.js": "^0.3.0",
"react": "18.2.0",
"react": "^18.3.1",
"react-bootstrap": "^2.7.2",
"react-bootstrap-toggle": "^2.3.2",
"react-dom": "18.2.0",
"react-dom": "^18.3.1",
"react-dropdown-tree-select": "^2.8.0",
"react-dropzone": "^14.2.3",
"react-redux": "^8.1.1",
"react-redux": "^9.1.2",
"react-render-if-visible": "^2.1.1",
"react-use": "^17.5.0",
"styled-components": "^5.3.9",
Expand All @@ -54,9 +54,9 @@
"@types/jest": "^29.5.0",
"@types/js-cookie": "^3.0.3",
"@types/node": "^18.16.0",
"@types/react": "^18.2.0",
"@types/react": "^18.3.1",
"@types/react-bootstrap": "^0.32.32",
"@types/react-dom": "^18.2.0",
"@types/react-dom": "^18.3.1",
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
Expand Down
42 changes: 22 additions & 20 deletions frontend/src/app/listenerMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Retrieved from https://redux-toolkit.js.org/api/createListenerMiddleware
*/

import type { TypedAddListener, TypedStartListening } from "@reduxjs/toolkit";
import {
addListener,
createListenerMiddleware,
Expand All @@ -25,7 +24,6 @@ import {
} from "@/features/invalidIdentifiers/invalidIdentifiersSlice";
import {
addMembers,
clearQueries,
selectProjectCardback,
setQueries,
setSelectedCardback,
Expand Down Expand Up @@ -55,12 +53,12 @@ import type { AppDispatch, RootState } from "./store";

export const listenerMiddleware = createListenerMiddleware();

export type AppStartListening = TypedStartListening<RootState, AppDispatch>;
const startAppListening = listenerMiddleware.startListening.withTypes<
RootState,
AppDispatch
>();

const startAppListening =
listenerMiddleware.startListening as AppStartListening;

const addAppListener = addListener as TypedAddListener<RootState, AppDispatch>;
const addAppListener = addListener.withTypes<RootState, AppDispatch>();

//# endregion

Expand Down Expand Up @@ -128,7 +126,7 @@ startAppListening({
const isBackendConfigured = selectBackendConfigured(state);
const searchSettingsSourcesValid = selectSearchSettingsSourcesValid(state);
if (isBackendConfigured && searchSettingsSourcesValid) {
await dispatch(clearSearchResults());
dispatch(clearSearchResults());
await fetchCardDocumentsAndReportError(dispatch);
}
},
Expand Down Expand Up @@ -182,18 +180,22 @@ startAppListening({
*/

// wait for all search results to load (removing this will cause a race condition)
await condition((action, currentState) => {
const { slots }: { slots: Array<[Faces, number]> } = action.payload;
return slots
.map(([face, slot]) => {
const searchQuery = currentState.project.members[slot][face]?.query;
return searchQuery?.query != null
? currentState.searchResults.searchResults[searchQuery.query][
searchQuery.card_type
] != null
: true;
})
.every((value) => value);
await condition((action, currentState): boolean => {
if (setQueries.match(action)) {
const { slots }: { slots: Array<[Faces, number]> } = action.payload;
return slots
.map(([face, slot]) => {
const searchQuery = currentState.project.members[slot][face]?.query;
return searchQuery?.query != null
? currentState.searchResults.searchResults[searchQuery.query][
searchQuery.card_type
] != null
: true;
})
.every((value) => value);
} else {
return true;
}
});

const state = getState();
Expand Down
39 changes: 27 additions & 12 deletions frontend/src/app/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { PreloadedState } from "@reduxjs/toolkit";
import type { Middleware, MiddlewareAPI } from "@reduxjs/toolkit";
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { isRejectedWithValue } from "@reduxjs/toolkit";
import { isAction, MiddlewareAPI } from "@reduxjs/toolkit";
import {
combineReducers,
configureStore,
isRejectedWithValue,
Tuple,
} from "@reduxjs/toolkit";

import { api } from "@/app/api";
import { listenerMiddleware } from "@/app/listenerMiddleware";
Expand Down Expand Up @@ -38,25 +41,36 @@ const rootReducer = combineReducers({

//# region middleware

const rtkQueryErrorLogger: Middleware =
(api: MiddlewareAPI) => (next) => (action) => {
const rtkQueryErrorLogger =
(api: MiddlewareAPI) =>
(next: (action: unknown) => unknown) =>
(action: unknown) => {
/**
* Whenever a RTK Query API request fails, display the response's error message to the user as a toast.
*/

if (!isAction(action)) {
return;
}

const backendConfigured = selectBackendConfigured(api.getState());
if (
backendConfigured &&
isRejectedWithValue(action) &&
action.payload?.data != null
action.payload != null
) {
// dispatch the error to the store for displaying in a toast to the user
const data = (
action.payload as {
data: { name: string | null; message: string | null };
}
).data;
api.dispatch(
setNotification([
action.type,
{
name: action.payload.data.name,
message: action.payload.data.message,
name: data.name,
message: data.message,
level: "error",
},
])
Expand All @@ -68,20 +82,21 @@ const rtkQueryErrorLogger: Middleware =

//# endregion

export const setupStore = (preloadedState?: PreloadedState<RootState>) => {
export const setupStore = (preloadedState?: Partial<RootState>) => {
return configureStore({
reducer: rootReducer,
preloadedState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.prepend(listenerMiddleware.middleware)
.concat([api.middleware, rtkQueryErrorLogger]),
.concat(new Tuple(api.middleware, rtkQueryErrorLogger)),
});
};

const store = setupStore();

export type AppStore = ReturnType<typeof setupStore>;
export type RootState = ReturnType<typeof rootReducer>;
export type AppDispatch = typeof store.dispatch;
export type AppDispatch = AppStore["dispatch"];

export default store;
3 changes: 1 addition & 2 deletions frontend/src/common/test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Retrieved from https://redux.js.org/usage/writing-tests
*/

import type { PreloadedState } from "@reduxjs/toolkit";
import { Store } from "@reduxjs/toolkit";
import { within } from "@testing-library/dom";
import type { RenderOptions } from "@testing-library/react";
Expand All @@ -24,7 +23,7 @@ import { LayoutWithoutReduxProvider } from "@/features/ui/layout";
// This type interface extends the default options for render from RTL, as well
// as allows the user to specify other things such as initialState, store.
interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
preloadedState?: PreloadedState<RootState>;
preloadedState?: Partial<RootState>;
store?: Store;
}

Expand Down
22 changes: 14 additions & 8 deletions frontend/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";

import type { AppDispatch, RootState } from "@/app/store";
import {
asyncThunkCreator,
buildCreateSlice,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { useDispatch, useSelector, useStore } from "react-redux";

import type { AppDispatch, AppStore, RootState } from "@/app/store";
import { CSVHeaders } from "@/common/constants";
import { SearchQuery } from "@/common/schema_types";
export type {
Expand All @@ -13,15 +17,17 @@ export type {
SourceSettings,
} from "@/common/schema_types";

type DispatchFunc = () => AppDispatch;
export const useAppDispatch: DispatchFunc = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();
export const useAppStore = useStore.withTypes<AppStore>();
export const createAppSlice = buildCreateSlice({
creators: { asyncThunk: asyncThunkCreator },
});

export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
rejectValue: string;
extra: { s: string; n: number };
}>();

export type ThunkStatus = "idle" | "loading" | "succeeded" | "failed";
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/features/backend/backendSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@
* State management for the backend that the app should communicate with as configured by the user.
*/

import { createSlice } from "@reduxjs/toolkit";

import { useGetBackendInfoQuery } from "@/app/api";
import { RootState } from "@/app/store";
import { ProjectName } from "@/common/constants";
import { BackendState, useAppSelector } from "@/common/types";
import { BackendState, createAppSlice, useAppSelector } from "@/common/types";

//# region slice configuration

const initialState: BackendState = {
url: null,
};

export const backendSlice = createSlice({
export const backendSlice = createAppSlice({
name: "backend",
initialState,
reducers: {
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/features/card/cardbackSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
* State management for cardbacks retrieved from the backend.
*/

import { createSlice } from "@reduxjs/toolkit";

import { APIGetCardbacks } from "@/app/api";
import { AppDispatch, RootState } from "@/app/store";
import { CardbacksState, createAppAsyncThunk } from "@/common/types";
import {
CardbacksState,
createAppAsyncThunk,
createAppSlice,
} from "@/common/types";
import { selectBackendURL } from "@/features/backend/backendSlice";
import { selectSearchSettings } from "@/features/searchSettings/searchSettingsSlice";
import { setNotification } from "@/features/toasts/toastsSlice";
Expand Down Expand Up @@ -51,15 +53,15 @@ const initialState: CardbacksState = {
error: null,
};

export const cardbackSlice = createSlice({
export const cardbackSlice = createAppSlice({
name: "cardbacks",
initialState,
reducers: {
addCardbackDocuments: (state, action) => {
state.cardbacks = [...action.payload];
},
},
extraReducers(builder) {
extraReducers: (builder) => {
builder
.addCase(fetchCardbacks.pending, (state, action) => {
state.status = "loading";
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/features/export/exportDecklist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { saveAs } from "file-saver";
import React from "react";
import Dropdown from "react-bootstrap/Dropdown";
import { useStore } from "react-redux";

import { RootState } from "@/app/store";
import { Back, Card, FaceSeparator, Front } from "@/common/constants";
Expand All @@ -16,6 +15,7 @@ import {
ProjectMember,
SlotProjectMembers,
useAppSelector,
useAppStore,
} from "@/common/types";
import { RightPaddedIcon } from "@/components/icon";
import {
Expand Down Expand Up @@ -120,17 +120,15 @@ const selectGeneratedDecklist = (state: RootState): string => {
export function ExportDecklist() {
//# region queries and hooks

const store = useStore();
const store = useAppStore();
const isProjectEmpty = useAppSelector(selectIsProjectEmpty);

//# endregion

//# region callbacks

const downloadFile = () => {
const generatedDecklist = selectGeneratedDecklist(
store.getState() as RootState
);
const generatedDecklist = selectGeneratedDecklist(store.getState());
saveAs(
new Blob([generatedDecklist], { type: "text/plain;charset=utf-8" }),
"decklist.txt" // TODO: use project name here when we eventually track that
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/export/exportXML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import { saveAs } from "file-saver";
import React from "react";
import Dropdown from "react-bootstrap/Dropdown";
import { useStore } from "react-redux";
import formatXML from "xml-formatter";

import { RootState } from "@/app/store";
import { Back, Front, ReversedCardTypePrefixes } from "@/common/constants";
import { useAppStore } from "@/common/types";
import {
CardDocuments,
FinishSettingsState,
Expand Down Expand Up @@ -208,10 +208,10 @@ export function generateXML(
}

export function useExportXML() {
const store = useStore();
const store = useAppStore();

return () => {
const generatedXML = selectGeneratedXML(store.getState() as RootState);
const generatedXML = selectGeneratedXML(store.getState());
saveAs(
new Blob([generatedXML], { type: "text/xml;charset=utf-8" }),
"cards.xml"
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/finishSettings/finishSettingsSlice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { PayloadAction } from "@reduxjs/toolkit";

import { RootState } from "@/app/store";
import { CardstockFoilCompatibility } from "@/common/constants";
import { Cardstock, FinishSettingsState } from "@/common/types";
import { Cardstock, createAppSlice, FinishSettingsState } from "@/common/types";

//# region slice configuration

Expand All @@ -11,7 +11,7 @@ const initialState: FinishSettingsState = {
foil: false,
};

export const finishSettingsSlice = createSlice({
export const finishSettingsSlice = createAppSlice({
name: "finishSettings",
initialState,
reducers: {
Expand Down
Loading

0 comments on commit ed037f2

Please sign in to comment.