Skip to content

Commit 8d24f8b

Browse files
authored
Migrate to Redux Toolkit part 4 (#2843)
* Migrate Achievement actions to RTK * Migrate Persistence actions to RTK * Migrate remaining Stories actions to RTK * Revert "Migrate Achievement actions to RTK" This reverts commit 66d0f4a. * Migrate some workspace reducers to new reducer * Migrate more workspace reducers to new reducer * Begin StoriesReducer migration * Create new reducer using `createReducer` from RTK * Migrate some reducers to new reducer * Create own copy of ActionType helper Facilitates the migration away from `typesafe-actions` package. * Create and use `generateAction` function in tests Part of migrating away from `typesafe-actions`. * Migrate most Achievement actions to RTK One remaining action creator is left to use the old library due to type errors that are left to investigate in a future commit. * Migrate FileSystemReducer to RTK * Migrate AchievementReducer to RTK * Update reducer typings Done in preparation for migration to RTK v2 and Redux v5. Also improves type safety of reducers. * Fix type errors with reducer tests Improves overall type-safety following the new reducer typings. * Fix SessionReducer tests * Fix format
1 parent c7fe3b4 commit 8d24f8b

File tree

21 files changed

+311
-281
lines changed

21 files changed

+311
-281
lines changed

src/commons/application/reducers/CommonsReducer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { SourceActionType } from 'src/commons/utils/ActionsHelper';
44
import { defaultRouter } from '../ApplicationTypes';
55
import { RouterState, UPDATE_REACT_ROUTER } from '../types/CommonsTypes';
66

7-
export const RouterReducer: Reducer<RouterState> = (
7+
export const RouterReducer: Reducer<RouterState, SourceActionType> = (
88
state = defaultRouter,
9-
action: SourceActionType
9+
action
1010
) => {
1111
switch (action.type) {
1212
case UPDATE_REACT_ROUTER:

src/commons/application/reducers/SessionsReducer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ import {
2929
UPDATE_TOTAL_XP
3030
} from '../types/SessionTypes';
3131

32-
export const SessionsReducer: Reducer<SessionState> = (
32+
export const SessionsReducer: Reducer<SessionState, SourceActionType> = (
3333
state = defaultSession,
34-
action: SourceActionType
34+
action
3535
) => {
3636
switch (action.type) {
3737
case LOG_OUT:

src/commons/application/reducers/__tests__/SessionReducer.ts

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('LOG_OUT works correctly on default session', () => {
3131
const action = {
3232
type: LOG_OUT,
3333
payload: {}
34-
};
34+
} as const;
3535
const result: SessionState = SessionsReducer(defaultSession, action);
3636

3737
expect(result).toEqual(defaultSession);
@@ -47,7 +47,7 @@ test('SET_TOKEN sets accessToken and refreshToken correctly', () => {
4747
accessToken,
4848
refreshToken
4949
}
50-
};
50+
} as const;
5151
const result: SessionState = SessionsReducer(defaultSession, action);
5252

5353
expect(result).toEqual({
@@ -58,28 +58,30 @@ test('SET_TOKEN sets accessToken and refreshToken correctly', () => {
5858

5959
test('SET_USER works correctly', () => {
6060
const payload = {
61+
userId: 123,
6162
name: 'test student',
62-
role: Role.Student,
6363
courses: [
6464
{
6565
courseId: 1,
6666
courseName: `CS1101 Programming Methodology (AY20/21 Sem 1)`,
6767
courseShortName: `CS1101S`,
68-
viewable: true
68+
viewable: true,
69+
role: Role.Student
6970
},
7071
{
7172
courseId: 2,
7273
courseName: `CS2030S Programming Methodology II (AY20/21 Sem 2)`,
7374
courseShortName: `CS2030S`,
74-
viewable: true
75+
viewable: true,
76+
role: Role.Staff
7577
}
7678
]
7779
};
7880

7981
const action = {
8082
type: SET_USER,
8183
payload
82-
};
84+
} as const;
8385
const result: SessionState = SessionsReducer(defaultSession, action);
8486

8587
expect(result).toEqual({
@@ -105,7 +107,7 @@ test('SET_COURSE_CONFIGURATION works correctly', () => {
105107
const action = {
106108
type: SET_COURSE_CONFIGURATION,
107109
payload
108-
};
110+
} as const;
109111
const result: SessionState = SessionsReducer(defaultSession, action);
110112

111113
expect(result).toEqual({
@@ -135,7 +137,7 @@ test('SET_COURSE_REGISTRATION works correctly', () => {
135137
const action = {
136138
type: SET_COURSE_REGISTRATION,
137139
payload
138-
};
140+
} as const;
139141
const result: SessionState = SessionsReducer(defaultSession, action);
140142

141143
expect(result).toEqual({
@@ -153,7 +155,10 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
153155
buildSolution: false,
154156
isContest: false,
155157
hoursBeforeEarlyXpDecay: 48,
156-
earlySubmissionXp: 200
158+
earlySubmissionXp: 200,
159+
isManuallyGraded: false,
160+
displayInDashboard: true,
161+
hasTokenCounter: false
157162
},
158163
{
159164
assessmentConfigId: 1,
@@ -162,7 +167,10 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
162167
buildSolution: false,
163168
isContest: false,
164169
hoursBeforeEarlyXpDecay: 48,
165-
earlySubmissionXp: 200
170+
earlySubmissionXp: 200,
171+
isManuallyGraded: false,
172+
displayInDashboard: true,
173+
hasTokenCounter: false
166174
},
167175
{
168176
assessmentConfigId: 1,
@@ -171,14 +179,17 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
171179
buildSolution: false,
172180
isContest: false,
173181
hoursBeforeEarlyXpDecay: 48,
174-
earlySubmissionXp: 200
182+
earlySubmissionXp: 200,
183+
isManuallyGraded: false,
184+
displayInDashboard: true,
185+
hasTokenCounter: false
175186
}
176187
];
177188

178189
const action = {
179190
type: SET_ASSESSMENT_CONFIGURATIONS,
180191
payload
181-
};
192+
} as const;
182193
const result: SessionState = SessionsReducer(defaultSession, action);
183194

184195
expect(result).toEqual({
@@ -193,20 +204,22 @@ test('SET_ADMIN_PANEL_COURSE_REGISTRATIONS works correctly', () => {
193204
courseRegId: 1,
194205
courseId: 1,
195206
name: 'Bob',
207+
username: 'E1234567',
196208
role: Role.Student
197209
},
198210
{
199211
courseRegId: 2,
200212
courseId: 1,
201213
name: 'Avenger',
214+
username: 'E7654321',
202215
role: Role.Staff
203216
}
204217
];
205218

206219
const action = {
207220
type: SET_ADMIN_PANEL_COURSE_REGISTRATIONS,
208221
payload
209-
};
222+
} as const;
210223
const result: SessionState = SessionsReducer(defaultSession, action);
211224

212225
expect(result).toEqual({
@@ -220,7 +233,7 @@ test('SET_GITHUB_ACCESS_TOKEN works correctly', () => {
220233
const action = {
221234
type: SET_GITHUB_ACCESS_TOKEN,
222235
payload: token
223-
};
236+
} as const;
224237
const result: SessionState = SessionsReducer(defaultSession, action);
225238

226239
expect(result).toEqual({
@@ -267,7 +280,7 @@ test('UPDATE_ASSESSMENT works correctly in inserting assessment', () => {
267280
const action = {
268281
type: UPDATE_ASSESSMENT,
269282
payload: assessmentTest1
270-
};
283+
} as const;
271284
const resultMap: Map<number, Assessment> = SessionsReducer(defaultSession, action).assessments;
272285

273286
expect(resultMap.get(assessmentTest1.id)).toEqual(assessmentTest1);
@@ -285,7 +298,7 @@ test('UPDATE_ASSESSMENT works correctly in inserting assessment and retains old
285298
const action = {
286299
type: UPDATE_ASSESSMENT,
287300
payload: assessmentTest2
288-
};
301+
} as const;
289302
const resultMap: Map<number, Assessment> = SessionsReducer(newDefaultSession, action).assessments;
290303

291304
expect(resultMap.get(assessmentTest2.id)).toEqual(assessmentTest2);
@@ -303,7 +316,7 @@ test('UPDATE_ASSESSMENT works correctly in updating assessment', () => {
303316
const action = {
304317
type: UPDATE_ASSESSMENT,
305318
payload: assessmentTest2
306-
};
319+
} as const;
307320
const resultMap: Map<number, Assessment> = SessionsReducer(newDefaultSession, action).assessments;
308321

309322
expect(resultMap.get(assessmentTest2.id)).toEqual(assessmentTest2);
@@ -351,7 +364,7 @@ test('UPDATE_ASSESSMENT_OVERVIEWS works correctly in inserting assessment overvi
351364
const action = {
352365
type: UPDATE_ASSESSMENT_OVERVIEWS,
353366
payload: assessmentOverviewsTest1
354-
};
367+
} as const;
355368

356369
const result: SessionState = SessionsReducer(defaultSession, action);
357370

@@ -370,7 +383,7 @@ test('UPDATE_ASSESSMENT_OVERVIEWS works correctly in updating assessment overvie
370383
const action = {
371384
type: UPDATE_ASSESSMENT_OVERVIEWS,
372385
payload: assessmentOverviewsPayload
373-
};
386+
} as const;
374387

375388
const result: SessionState = SessionsReducer(newDefaultSession, action);
376389

@@ -445,7 +458,7 @@ test('UPDATE_GRADING works correctly in inserting gradings', () => {
445458
submissionId,
446459
grading: gradingTest1
447460
}
448-
};
461+
} as const;
449462

450463
const gradingMap: Map<number, GradingQuery> = SessionsReducer(defaultSession, action).gradings;
451464
expect(gradingMap.get(submissionId)).toEqual(gradingTest1);
@@ -468,7 +481,7 @@ test('UPDATE_GRADING works correctly in inserting gradings and retains old data'
468481
submissionId: submissionId2,
469482
grading: gradingTest2
470483
}
471-
};
484+
} as const;
472485

473486
const gradingMap: Map<number, GradingQuery> = SessionsReducer(newDefaultSession, action).gradings;
474487
expect(gradingMap.get(submissionId1)).toEqual(gradingTest1);
@@ -490,7 +503,7 @@ test('UPDATE_GRADING works correctly in updating gradings', () => {
490503
submissionId,
491504
grading: gradingTest2
492505
}
493-
};
506+
} as const;
494507

495508
const gradingMap: Map<number, GradingQuery> = SessionsReducer(newDefaultSession, action).gradings;
496509
expect(gradingMap.get(submissionId)).toEqual(gradingTest2);
@@ -546,11 +559,17 @@ const gradingOverviewTest2: GradingOverview[] = [
546559
test('UPDATE_GRADING_OVERVIEWS works correctly in inserting grading overviews', () => {
547560
const action = {
548561
type: UPDATE_GRADING_OVERVIEWS,
549-
payload: gradingOverviewTest1
550-
};
562+
payload: {
563+
count: gradingOverviewTest1.length,
564+
data: gradingOverviewTest1
565+
}
566+
} as const;
551567
const result: SessionState = SessionsReducer(defaultSession, action);
552568

553-
expect(result.gradingOverviews).toEqual(gradingOverviewTest1);
569+
expect(result.gradingOverviews).toEqual({
570+
count: gradingOverviewTest1.length,
571+
data: gradingOverviewTest1
572+
});
554573
});
555574

556575
test('UPDATE_GRADING_OVERVIEWS works correctly in updating grading overviews', () => {
@@ -561,11 +580,14 @@ test('UPDATE_GRADING_OVERVIEWS works correctly in updating grading overviews', (
561580
data: gradingOverviewTest1
562581
}
563582
};
564-
const gradingOverviewsPayload = [...gradingOverviewTest2, ...gradingOverviewTest1];
583+
const gradingOverviewsPayload = {
584+
count: gradingOverviewTest1.length + gradingOverviewTest2.length,
585+
data: [...gradingOverviewTest2, ...gradingOverviewTest1]
586+
};
565587
const action = {
566588
type: UPDATE_GRADING_OVERVIEWS,
567589
payload: gradingOverviewsPayload
568-
};
590+
} as const;
569591
const result: SessionState = SessionsReducer(newDefaultSession, action);
570592

571593
expect(result.gradingOverviews).toEqual(gradingOverviewsPayload);
@@ -592,7 +614,7 @@ test('UPDATE_NOTIFICATIONS works correctly in updating notifications', () => {
592614
const action = {
593615
type: UPDATE_NOTIFICATIONS,
594616
payload: notifications
595-
};
617+
} as const;
596618

597619
const result: SessionState = SessionsReducer(defaultSession, action);
598620

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1+
import { createReducer } from '@reduxjs/toolkit';
12
import { Reducer } from 'redux';
23

34
import { defaultFileSystem } from '../application/ApplicationTypes';
45
import { SourceActionType } from '../utils/ActionsHelper';
5-
import { FileSystemState, SET_IN_BROWSER_FILE_SYSTEM } from './FileSystemTypes';
6+
import { setInBrowserFileSystem } from './FileSystemActions';
7+
import { FileSystemState } from './FileSystemTypes';
68

7-
export const FileSystemReducer: Reducer<FileSystemState> = (
8-
state = defaultFileSystem,
9-
action: SourceActionType
10-
) => {
11-
switch (action.type) {
12-
case SET_IN_BROWSER_FILE_SYSTEM:
13-
return {
14-
...state,
15-
inBrowserFileSystem: action.payload.inBrowserFileSystem
16-
};
17-
default:
18-
return state;
9+
export const FileSystemReducer: Reducer<FileSystemState, SourceActionType> = createReducer(
10+
defaultFileSystem,
11+
builder => {
12+
builder.addCase(setInBrowserFileSystem, (state, action) => {
13+
state.inBrowserFileSystem = action.payload.inBrowserFileSystem;
14+
});
1915
}
20-
};
16+
);

src/commons/fileSystem/__tests__/FileSystemReducer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('SET_IN_BROWSER_FILE_SYSTEM', () => {
1212
payload: {
1313
inBrowserFileSystem
1414
}
15-
};
15+
} as const;
1616
const result = FileSystemReducer(defaultFileSystem, action);
1717
expect(result).toEqual({
1818
...defaultFileSystem,

src/commons/utils/ActionsHelper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { ActionType } from 'typesafe-actions';
2-
31
import * as CommonsActions from '../../commons/application/actions/CommonsActions';
42
import * as InterpreterActions from '../../commons/application/actions/InterpreterActions';
53
import * as SessionActions from '../../commons/application/actions/SessionActions';
@@ -19,6 +17,7 @@ import * as SourcecastActions from '../../features/sourceRecorder/sourcecast/Sou
1917
import * as SourceRecorderActions from '../../features/sourceRecorder/SourceRecorderActions';
2018
import * as SourcereelActions from '../../features/sourceRecorder/sourcereel/SourcereelActions';
2119
import * as StoriesActions from '../../features/stories/StoriesActions';
20+
import { ActionType } from './TypeHelper';
2221

2322
export const actions = {
2423
...AchievementActions,

src/commons/utils/TypeHelper.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ export type KeysOfType<O, T> = {
1010
[K in keyof O]: O[K] extends T ? K : never;
1111
}[keyof O];
1212

13+
// Adapted from https://github.com/piotrwitek/typesafe-actions/blob/a1fe54bb150ac1b935bb9ca78361d2d024d2efaf/src/type-helpers.ts#L117-L130
14+
export type ActionType<T extends Record<string, any>> = {
15+
[k in keyof T]: ReturnType<T[k]>;
16+
}[keyof T];
17+
1318
/* =========================================
1419
* Utility types for tuple type manipulation
1520
* ========================================= */

0 commit comments

Comments
 (0)