From 00ddfefcdf1208daf4d04af42c978340f4fbe043 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Sat, 6 Apr 2024 18:24:33 +0900 Subject: [PATCH 01/11] migrate: @/reducers/timetable/list.ts --- src/reducers/timetable/{list.js => list.ts} | 75 ++++++++++++--------- 1 file changed, 42 insertions(+), 33 deletions(-) rename src/reducers/timetable/{list.js => list.ts} (58%) diff --git a/src/reducers/timetable/list.js b/src/reducers/timetable/list.ts similarity index 58% rename from src/reducers/timetable/list.js rename to src/reducers/timetable/list.ts index 07bb1ae..63b8d9f 100644 --- a/src/reducers/timetable/list.js +++ b/src/reducers/timetable/list.ts @@ -7,17 +7,28 @@ import { ADD_LECTURE_TO_CART, DELETE_LECTURE_FROM_CART, SET_MOBILE_IS_LECTURE_LIST_OPEN, -} from '../../actions/timetable/list'; - -import { unique } from '../../utils/commonUtils'; + LectureListAction, +} from '@/actions/timetable/list'; +import { unique } from '@/utils/commonUtils'; import { LectureListCode } from '@/shapes/enum'; +import Lecture from '@/shapes/model/subject/Lecture'; -const initialState = { +interface ListState { + selectedListCode: LectureListCode; + lists: { + [key in LectureListCode]: { + lectureGroups: Lecture[][] | null; // null when not loaded, empty array when loaded but no lectures + }; + }; + isLectureListOpenOnMobile: boolean; +} + +const initialState: ListState = { selectedListCode: LectureListCode.SEARCH, lists: { [LectureListCode.SEARCH]: { - lectureGroups: [], + lectureGroups: [], // show empty array by default for search, instead of null }, [LectureListCode.BASIC]: { lectureGroups: null, @@ -32,26 +43,23 @@ const initialState = { isLectureListOpenOnMobile: false, }; -const list = (state = initialState, action) => { - const groupLectures = (lectures) => { - if (lectures.length === 0) { - return []; - } - +const list = (state = initialState, action: LectureListAction) => { + const groupLectures = (lectures: Lecture[]) => { const sortedLectures = lectures.sort((a, b) => { if (a.old_code !== b.old_code) { return a.old_code > b.old_code ? 10 : -10; } return a.class_no > b.class_no ? 1 : -1; }); - const courseIds = unique(sortedLectures.map((l) => l.course)); + const courseIds: number[] = unique(sortedLectures.map((l) => l.course)); const lectureGroups = courseIds - .map((c) => sortedLectures.filter((l) => l.course === c)) - .filter((c) => c.length > 0); + .map((course) => sortedLectures.filter((l) => l.course === course)) + .filter((lectureGroup) => lectureGroup.length > 0); return lectureGroups; }; - const ungroupLectureGroups = (lectureGroups) => lectureGroups.flat(1); + const ungroupLectureGroups = (lectureGroups: Lecture[][] | null) => + lectureGroups ? lectureGroups.flat(1) : []; switch (action.type) { case RESET: { @@ -67,27 +75,32 @@ const list = (state = initialState, action) => { newState.lists = { ...newState.lists }; newState.lists[action.code] = { ...newState.lists[action.code] }; newState.lists[action.code].lectureGroups = groupLectures(action.lectures); - return Object.assign({}, state, newState); + return newState; } + /** + * This is used to clear all lectures in all lists. + * Only search list becomes empty array, others become null. + * @example { SEARCH: [Lecture1, Lecture2, ...], BASIC: [Lecture3, Lecture4, ...], ... } => { SEARCH: [], BASIC: null, ... + */ case CLEAR_ALL_LISTS_LECTURES: { const newState = { ...state }; newState.lists = { ...newState.lists }; - Object.keys(newState.lists).forEach((k) => { - newState.lists[k] = { ...newState.lists[k] }; - if (k === LectureListCode.SEARCH) { - newState.lists[k].lectureGroups = []; - } else { - newState.lists[k].lectureGroups = null; - } + // keys can be not only LectureListCode but also FavoriteDepartment which set in FavoriteDepartmentsSubSection. + const keys = Object.keys(newState.lists) as Array; + keys.forEach((k) => { + newState.lists[k as keyof typeof newState.lists].lectureGroups = + k === LectureListCode.SEARCH ? [] : null; }); - return Object.assign({}, state, newState); + return newState; } + /** + * This is used to make search result null before getting new search result. + * @example { SEARCH: [Lecture1, Lecture2, ...], BASIC: [...], ... } => { SEARCH: null, BASIC: [...], ... } + */ case CLEAR_SEARCH_LIST_LECTURES: { const newState = { ...state }; - newState.lists = { ...newState.lists }; - newState.lists[LectureListCode.SEARCH] = { ...newState.lists[LectureListCode.SEARCH] }; newState.lists[LectureListCode.SEARCH].lectureGroups = null; - return Object.assign({}, state, newState); + return newState; } case ADD_LECTURE_TO_CART: { const { lectureGroups } = state.lists[LectureListCode.CART]; @@ -95,10 +108,8 @@ const list = (state = initialState, action) => { const newLectures = [...lectures, action.lecture]; const newLectureGroups = groupLectures(newLectures); const newState = { ...state }; - newState.lists = { ...newState.lists }; - newState.lists[LectureListCode.CART] = { ...newState.lists[LectureListCode.CART] }; newState.lists[LectureListCode.CART].lectureGroups = newLectureGroups; - return Object.assign({}, state, newState); + return newState; } case DELETE_LECTURE_FROM_CART: { const { lectureGroups } = state.lists[LectureListCode.CART]; @@ -106,10 +117,8 @@ const list = (state = initialState, action) => { const newLectures = lectures.filter((l) => l.id !== action.lecture.id); const newLectureGroups = groupLectures(newLectures); const newState = { ...state }; - newState.lists = { ...newState.lists }; - newState.lists[LectureListCode.CART] = { ...newState.lists[LectureListCode.CART] }; newState.lists[LectureListCode.CART].lectureGroups = newLectureGroups; - return Object.assign({}, state, newState); + return newState; } case SET_MOBILE_IS_LECTURE_LIST_OPEN: { return Object.assign({}, state, { From 7b1fe469d915962e4d3cc2a00a1facadc4d7d848 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Sun, 7 Apr 2024 15:34:39 +0900 Subject: [PATCH 02/11] migrate: @/reducers/timetable/search.ts --- src/reducers/timetable/{search.js => search.ts} | 17 ++++++++++++++--- src/shapes/enum.ts | 10 ++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) rename src/reducers/timetable/{search.js => search.ts} (72%) diff --git a/src/reducers/timetable/search.js b/src/reducers/timetable/search.ts similarity index 72% rename from src/reducers/timetable/search.js rename to src/reducers/timetable/search.ts index ab50737..72602ab 100644 --- a/src/reducers/timetable/search.js +++ b/src/reducers/timetable/search.ts @@ -5,9 +5,20 @@ import { SET_LAST_SEARCH_OPTION, SET_CLASSTIME_OPTIONS, CLEAR_CLASSTIME_OPTIONS, -} from '../../actions/timetable/search'; + SearchAction, +} from '@/actions/timetable/search'; +import { Day } from '@/shapes/enum'; +import LectureLastSearchOption from '@/shapes/state/timetable/LectureLastSearchOption'; -const initialState = { +interface SearchState { + open: boolean; + lastSearchOption: LectureLastSearchOption; + classtimeBegin: number | null; + classtimeEnd: number | null; + classtimeDay: Day | null; +} + +const initialState: SearchState = { open: true, lastSearchOption: {}, classtimeBegin: null, @@ -15,7 +26,7 @@ const initialState = { classtimeDay: null, }; -const search = (state = initialState, action) => { +const search = (state = initialState, action: SearchAction) => { switch (action.type) { case RESET: { return initialState; diff --git a/src/shapes/enum.ts b/src/shapes/enum.ts index 5adb488..06b28f9 100644 --- a/src/shapes/enum.ts +++ b/src/shapes/enum.ts @@ -67,3 +67,13 @@ export const enum LectureListCode { HUMANITY, CART, } + +export const enum Day { + MON, + TUE, + WED, + THU, + FRI, + SAT, + SUN, +} From db096ed93bb1825866e10d8c90441e32bf6ccf74 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Sun, 7 Apr 2024 15:44:40 +0900 Subject: [PATCH 03/11] migrate: @/reducers/timetable/semester.ts --- src/reducers/timetable/semester.js | 25 ------------------------ src/reducers/timetable/semester.ts | 31 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 25 deletions(-) delete mode 100644 src/reducers/timetable/semester.js create mode 100644 src/reducers/timetable/semester.ts diff --git a/src/reducers/timetable/semester.js b/src/reducers/timetable/semester.js deleted file mode 100644 index 4466e79..0000000 --- a/src/reducers/timetable/semester.js +++ /dev/null @@ -1,25 +0,0 @@ -import { RESET, SET_SEMESTER } from '../../actions/timetable/semester'; - -const initialState = { - year: null, - semester: null, -}; - -const semester = (state = initialState, action) => { - switch (action.type) { - case RESET: { - return initialState; - } - case SET_SEMESTER: { - return Object.assign({}, state, { - year: action.year, - semester: action.semester, - }); - } - default: { - return state; - } - } -}; - -export default semester; diff --git a/src/reducers/timetable/semester.ts b/src/reducers/timetable/semester.ts new file mode 100644 index 0000000..62936eb --- /dev/null +++ b/src/reducers/timetable/semester.ts @@ -0,0 +1,31 @@ +import { RESET, SET_SEMESTER, SemesterAction } from '@/actions/timetable/semester'; +import { SemesterType } from '@/shapes/enum'; + +interface SemesterState { + year: number | null; + semester: SemesterType | null; +} + +const initialState: SemesterState = { + year: null, + semester: null, +}; + +const semester = (state = initialState, action: SemesterAction) => { + switch (action.type) { + case RESET: { + return initialState; + } + case SET_SEMESTER: { + return { + year: action.year, + semester: action.semester, + }; + } + default: { + return state; + } + } +}; + +export default semester; From bd3e57c9b3942c01009e21a2f9a63f9f9380d1ef Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Mon, 8 Apr 2024 18:29:02 +0900 Subject: [PATCH 04/11] migrate: @/reducers/timetable/timetable.ts --- .../timetable/{timetable.js => timetable.ts} | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) rename src/reducers/timetable/{timetable.js => timetable.ts} (72%) diff --git a/src/reducers/timetable/timetable.js b/src/reducers/timetable/timetable.ts similarity index 72% rename from src/reducers/timetable/timetable.js rename to src/reducers/timetable/timetable.ts index a11623f..cf409dd 100644 --- a/src/reducers/timetable/timetable.js +++ b/src/reducers/timetable/timetable.ts @@ -1,3 +1,5 @@ +import { MyPseudoTimetable } from '@/shapes/model/timetable/Timetable'; +import Timetable from '@/shapes/model/timetable/Timetable'; import { RESET, SET_TIMETABLES, @@ -13,11 +15,22 @@ import { UPDATE_CELL_SIZE, SET_IS_DRAGGING, SET_MOBILE_IS_TIMETABLE_TABS_OPEN, + TimetableAction, } from '../../actions/timetable/timetable'; const MY = -1; -const initialState = { +interface TimetableState { + timetables: Timetable[] | null; + myTimetable: MyPseudoTimetable; + selectedTimetable: Timetable | null; + cellWidth: number; + cellHeight: number; + isDragging: boolean; + isTimetableTabsOpenOnMobile: boolean; +} + +const initialState: TimetableState = { timetables: null, myTimetable: { id: MY, @@ -31,25 +44,32 @@ const initialState = { isTimetableTabsOpenOnMobile: false, }; -const timetable = (state = initialState, action) => { +const timetable = (state = initialState, action: TimetableAction) => { switch (action.type) { case RESET: { return initialState; } + /** + * Set timetables of this semester when the first time timetable is loaded or the semester is changed. + * If the selectedTimetable is not myTimetable, it will be updated to the first timetable of the semester. + */ case SET_TIMETABLES: { return Object.assign({}, state, { timetables: action.timetables, selectedTimetable: - state.selectedTimetable && state.selectedTimetable.id === state.myTimetable.id + state.selectedTimetable?.id === state.myTimetable.id ? state.selectedTimetable : action.timetables[0], }); } + /** + * Clear timetables before fetching new timetables when semester changes. + */ case CLEAR_TIMETABLES: { return Object.assign({}, state, { timetables: null, selectedTimetable: - state.selectedTimetable && state.selectedTimetable.id === state.myTimetable.id + state.selectedTimetable?.id === state.myTimetable.id ? { ...state.selectedTimetable, lectures: [], @@ -59,12 +79,14 @@ const timetable = (state = initialState, action) => { } case SET_MY_TIMETABLE_LECTURES: { return Object.assign({}, state, { + // Update myTimetable with lectures myTimetable: { ...state.myTimetable, lectures: action.lectures, }, + // Update selectedTimetable with lectures if myTimetable is selected selectedTimetable: - state.selectedTimetable && state.selectedTimetable.id === state.myTimetable.id + state.selectedTimetable?.id === state.myTimetable.id ? { ...state.selectedTimetable, lectures: action.lectures, @@ -72,12 +94,18 @@ const timetable = (state = initialState, action) => { : state.selectedTimetable, }); } + /** + * Set selectedTimetable when the user selects a timetable tab. + */ case SET_SELECTED_TIMETABLE: { return Object.assign({}, state, { selectedTimetable: action.timetable, }); } case CREATE_TIMETABLE: { + if (!state.timetables) { + return state; + } const newArrangeOrder = state.timetables.length > 0 ? Math.max(...state.timetables.map((t) => t.arrange_order)) + 1 @@ -93,10 +121,13 @@ const timetable = (state = initialState, action) => { }); } case DELETE_TIMETABLE: { + if (!state.timetables) { + return state; + } const indexOfTable = state.timetables.findIndex((t) => t.id === action.timetable.id); const newTables = state.timetables.filter((t) => t.id !== action.timetable.id); const newSelectedTimetable = - indexOfTable !== state.timetables.length - 1 + indexOfTable !== state.timetables.length - 1 // If the deleted timetable is not the last one ? newTables[indexOfTable] : newTables[indexOfTable - 1]; return Object.assign({}, state, { @@ -105,9 +136,12 @@ const timetable = (state = initialState, action) => { }); } case DUPLICATE_TIMETABLE: { + if (!state.timetables) { + return state; + } const newTable = { id: action.id, - lectures: action.timetable.lectures.slice(), + lectures: action.timetable.lectures, arrange_order: Math.max(...state.timetables.map((t) => t.arrange_order)) + 1, }; return Object.assign({}, state, { @@ -116,6 +150,9 @@ const timetable = (state = initialState, action) => { }); } case ADD_LECTURE_TO_TIMETABLE: { + if (!state.timetables || !state.selectedTimetable) { + return state; + } const newTable = { ...state.selectedTimetable, lectures: state.selectedTimetable.lectures.concat([action.lecture]), @@ -127,6 +164,9 @@ const timetable = (state = initialState, action) => { }); } case REMOVE_LECTURE_FROM_TIMETABLE: { + if (!state.timetables || !state.selectedTimetable) { + return state; + } const newTable = { ...state.selectedTimetable, lectures: state.selectedTimetable.lectures.filter((l) => l.id !== action.lecture.id), @@ -138,6 +178,9 @@ const timetable = (state = initialState, action) => { }); } case REORDER_TIMETABLE: { + if (!state.timetables || !state.selectedTimetable || state.selectedTimetable.id === MY) { + return state; + } const newTables = state.timetables.map((t) => { if (t.id === action.timetable.id) { return { @@ -166,10 +209,7 @@ const timetable = (state = initialState, action) => { return t; }); newTables.sort((t1, t2) => t1.arrange_order - t2.arrange_order); - const updatedTable = - state.selectedTimetable.id === MY - ? state.selectedTimetable - : newTables.find((t) => t.id === state.selectedTimetable.id); + const updatedTable = newTables.find((t) => t.id === state.selectedTimetable?.id); return Object.assign({}, state, { timetables: newTables, selectedTimetable: updatedTable, From 9135bc5e7a9183327df57b8220c2716cd21206f5 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Mon, 8 Apr 2024 18:50:18 +0900 Subject: [PATCH 05/11] refactor: add `Detail` interface --- src/shapes/state/timetable/LectureFocus.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/shapes/state/timetable/LectureFocus.ts b/src/shapes/state/timetable/LectureFocus.ts index 166604c..bbfeabe 100644 --- a/src/shapes/state/timetable/LectureFocus.ts +++ b/src/shapes/state/timetable/LectureFocus.ts @@ -3,6 +3,11 @@ import type Lecture from '@/shapes/model/subject/Lecture'; import type Review from '@/shapes/model/review/Review'; type EmtpyArray = []; +export interface Detail { + lecture?: Lecture; + name: string; + info: string; +} interface FromNone { from: LectureFocusFrom.NONE; @@ -28,11 +33,7 @@ interface FromMutliple { lecture?: null; reviews?: null; multipleTitle: ''; - multipleDetails: { - lecture?: Lecture; - name: string; - info: string; - }[]; + multipleDetails: Detail[]; } type LectureFocus = FromNone | FromListOrTable | FromMutliple; From 61e44b27b76c16ac5ae63fcfd21a6d9cdae439d0 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Mon, 8 Apr 2024 18:50:49 +0900 Subject: [PATCH 06/11] migrate: @/reducers/timetable/lectureFocus.ts --- .../{lectureFocus.js => lectureFocus.ts} | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) rename src/reducers/timetable/{lectureFocus.js => lectureFocus.ts} (71%) diff --git a/src/reducers/timetable/lectureFocus.js b/src/reducers/timetable/lectureFocus.ts similarity index 71% rename from src/reducers/timetable/lectureFocus.js rename to src/reducers/timetable/lectureFocus.ts index 379c14b..7a250c5 100644 --- a/src/reducers/timetable/lectureFocus.js +++ b/src/reducers/timetable/lectureFocus.ts @@ -1,3 +1,4 @@ +import Review from '@/shapes/model/review/Review'; import { RESET, SET_LECTURE_FOCUS, @@ -5,11 +6,23 @@ import { SET_REVIEWS, SET_MULTIPLE_FOCUS, CLEAR_MULTIPLE_FOCUS, + LectureFocusAction, } from '../../actions/timetable/lectureFocus'; import { LectureFocusFrom } from '@/shapes/enum'; +import Lecture from '@/shapes/model/subject/Lecture'; +import { Detail } from '@/shapes/state/timetable/LectureFocus'; -const initialState = { +interface LectureFocusState { + from: LectureFocusFrom; + clicked: boolean; + lecture: Lecture | null; + reviews: Review[] | null; + multipleTitle: string; + multipleDetails: Detail[]; +} + +const initialState: LectureFocusState = { from: LectureFocusFrom.NONE, clicked: false, lecture: null, @@ -18,7 +31,7 @@ const initialState = { multipleDetails: [], }; -const lectureFocus = (state = initialState, action) => { +const lectureFocus = (state = initialState, action: LectureFocusAction) => { switch (action.type) { case RESET: { return initialState; @@ -33,7 +46,7 @@ const lectureFocus = (state = initialState, action) => { clicked: action.clicked, lecture: action.lecture, }, - lectureChanged ? { reviews: null } : {}, + lectureChanged ? { reviews: null } : {}, // if user clicked the same lecture again, don't clear reviews ); } case CLEAR_LECTURE_FOCUS: { From d0bd52e186bd0a35284d155ce1511f47a4f7a2f1 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Mon, 8 Apr 2024 18:59:02 +0900 Subject: [PATCH 07/11] migrate: @/reducers/timetable/index.ts --- src/reducers/timetable/{index.js => index.ts} | 1 + 1 file changed, 1 insertion(+) rename src/reducers/timetable/{index.js => index.ts} (86%) diff --git a/src/reducers/timetable/index.js b/src/reducers/timetable/index.ts similarity index 86% rename from src/reducers/timetable/index.js rename to src/reducers/timetable/index.ts index f16d6a1..c0348c8 100644 --- a/src/reducers/timetable/index.js +++ b/src/reducers/timetable/index.ts @@ -13,4 +13,5 @@ const CombinedReducer = combineReducers({ lectureFocus: lectureFocus, }); +export type TimetableState = ReturnType; export default CombinedReducer; From 4336a1665f1b88883a1bc43e1447907b7c644a43 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Sun, 14 Apr 2024 15:45:10 +0900 Subject: [PATCH 08/11] refactor: use spread syntax instead of Object.assign() --- src/reducers/timetable/lectureFocus.ts | 37 ++++++------- src/reducers/timetable/list.ts | 8 +-- src/reducers/timetable/search.ts | 25 ++++----- src/reducers/timetable/timetable.ts | 72 ++++++++------------------ 4 files changed, 48 insertions(+), 94 deletions(-) diff --git a/src/reducers/timetable/lectureFocus.ts b/src/reducers/timetable/lectureFocus.ts index 7a250c5..25bc787 100644 --- a/src/reducers/timetable/lectureFocus.ts +++ b/src/reducers/timetable/lectureFocus.ts @@ -38,43 +38,36 @@ const lectureFocus = (state = initialState, action: LectureFocusAction) => { } case SET_LECTURE_FOCUS: { const lectureChanged = !state.lecture || state.lecture.id !== action.lecture.id; - return Object.assign( - {}, - state, - { - from: action.from, - clicked: action.clicked, - lecture: action.lecture, - }, - lectureChanged ? { reviews: null } : {}, // if user clicked the same lecture again, don't clear reviews - ); + return { + ...state, + from: action.from, + clicked: action.clicked, + lecture: action.lecture, + reviews: lectureChanged ? null : state.reviews, + }; } case CLEAR_LECTURE_FOCUS: { - return Object.assign({}, state, { + return { + ...state, from: LectureFocusFrom.NONE, clicked: false, lecture: null, reviews: null, - }); + }; } case SET_REVIEWS: { - return Object.assign({}, state, { - reviews: action.reviews, - }); + return { ...state, reviews: action.reviews }; } case SET_MULTIPLE_FOCUS: { - return Object.assign({}, state, { + return { + ...state, from: LectureFocusFrom.MULTIPLE, multipleTitle: action.multipleTitle, multipleDetails: action.multipleDetails, - }); + }; } case CLEAR_MULTIPLE_FOCUS: { - return Object.assign({}, state, { - from: LectureFocusFrom.NONE, - multipleTitle: '', - multipleDetails: [], - }); + return { ...state, from: LectureFocusFrom.NONE, multipleTitle: '', multipleDetails: [] }; } default: { return state; diff --git a/src/reducers/timetable/list.ts b/src/reducers/timetable/list.ts index 63b8d9f..4fec1c9 100644 --- a/src/reducers/timetable/list.ts +++ b/src/reducers/timetable/list.ts @@ -66,9 +66,7 @@ const list = (state = initialState, action: LectureListAction) => { return initialState; } case SET_SELECTED_LIST_CODE: { - return Object.assign({}, state, { - selectedListCode: action.listCode, - }); + return { ...state, selectedListCode: action.listCode }; } case SET_LIST_LECTURES: { const newState = { ...state }; @@ -121,9 +119,7 @@ const list = (state = initialState, action: LectureListAction) => { return newState; } case SET_MOBILE_IS_LECTURE_LIST_OPEN: { - return Object.assign({}, state, { - isLectureListOpenOnMobile: action.isLectureListOpenOnMobile, - }); + return { ...state, isLectureListOpenOnMobile: action.isLectureListOpenOnMobile }; } default: { return state; diff --git a/src/reducers/timetable/search.ts b/src/reducers/timetable/search.ts index 72602ab..db0e1a1 100644 --- a/src/reducers/timetable/search.ts +++ b/src/reducers/timetable/search.ts @@ -9,6 +9,7 @@ import { } from '@/actions/timetable/search'; import { Day } from '@/shapes/enum'; import LectureLastSearchOption from '@/shapes/state/timetable/LectureLastSearchOption'; +import { stat } from 'fs'; interface SearchState { open: boolean; @@ -32,36 +33,30 @@ const search = (state = initialState, action: SearchAction) => { return initialState; } case OPEN_SEARCH: { - return Object.assign({}, state, { - open: true, - }); + return { ...state, open: true }; } case CLOSE_SEARCH: { - return Object.assign({}, state, { + return { + ...state, open: false, classtimeBegin: null, classtimeEnd: null, classtimeDay: null, - }); + }; } case SET_LAST_SEARCH_OPTION: { - return Object.assign({}, state, { - lastSearchOption: action.lastSearchOption, - }); + return { ...state, lastSearchOption: action.lastSearchOption }; } case SET_CLASSTIME_OPTIONS: { - return Object.assign({}, state, { + return { + ...state, classtimeBegin: action.classtimeBegin, classtimeEnd: action.classtimeEnd, classtimeDay: action.classtimeDay, - }); + }; } case CLEAR_CLASSTIME_OPTIONS: { - return Object.assign({}, state, { - classtimeBegin: null, - classtimeEnd: null, - classtimeDay: null, - }); + return { ...state, classtimeBegin: null, classtimeEnd: null, classtimeDay: null }; } default: { return state; diff --git a/src/reducers/timetable/timetable.ts b/src/reducers/timetable/timetable.ts index cf409dd..c79d2eb 100644 --- a/src/reducers/timetable/timetable.ts +++ b/src/reducers/timetable/timetable.ts @@ -54,53 +54,45 @@ const timetable = (state = initialState, action: TimetableAction) => { * If the selectedTimetable is not myTimetable, it will be updated to the first timetable of the semester. */ case SET_TIMETABLES: { - return Object.assign({}, state, { + return { + ...state, timetables: action.timetables, selectedTimetable: state.selectedTimetable?.id === state.myTimetable.id ? state.selectedTimetable : action.timetables[0], - }); + }; } /** * Clear timetables before fetching new timetables when semester changes. */ case CLEAR_TIMETABLES: { - return Object.assign({}, state, { + return { + ...state, timetables: null, selectedTimetable: state.selectedTimetable?.id === state.myTimetable.id - ? { - ...state.selectedTimetable, - lectures: [], - } + ? { ...state.selectedTimetable, lectures: [] } : null, - }); + }; } case SET_MY_TIMETABLE_LECTURES: { - return Object.assign({}, state, { + return { + ...state, // Update myTimetable with lectures - myTimetable: { - ...state.myTimetable, - lectures: action.lectures, - }, + myTimetable: { ...state.myTimetable, lectures: action.lectures }, // Update selectedTimetable with lectures if myTimetable is selected selectedTimetable: state.selectedTimetable?.id === state.myTimetable.id - ? { - ...state.selectedTimetable, - lectures: action.lectures, - } + ? { ...state.selectedTimetable, lectures: action.lectures } : state.selectedTimetable, - }); + }; } /** * Set selectedTimetable when the user selects a timetable tab. */ case SET_SELECTED_TIMETABLE: { - return Object.assign({}, state, { - selectedTimetable: action.timetable, - }); + return { ...state, selectedTimetable: action.timetable }; } case CREATE_TIMETABLE: { if (!state.timetables) { @@ -115,10 +107,7 @@ const timetable = (state = initialState, action: TimetableAction) => { lectures: [], arrange_order: newArrangeOrder, }; - return Object.assign({}, state, { - selectedTimetable: newTable, - timetables: [...state.timetables, newTable], - }); + return { ...state, selectedTimetable: newTable, timetables: [...state.timetables, newTable] }; } case DELETE_TIMETABLE: { if (!state.timetables) { @@ -130,10 +119,7 @@ const timetable = (state = initialState, action: TimetableAction) => { indexOfTable !== state.timetables.length - 1 // If the deleted timetable is not the last one ? newTables[indexOfTable] : newTables[indexOfTable - 1]; - return Object.assign({}, state, { - selectedTimetable: newSelectedTimetable, - timetables: newTables, - }); + return { ...state, selectedTimetable: newSelectedTimetable, timetables: newTables }; } case DUPLICATE_TIMETABLE: { if (!state.timetables) { @@ -144,10 +130,7 @@ const timetable = (state = initialState, action: TimetableAction) => { lectures: action.timetable.lectures, arrange_order: Math.max(...state.timetables.map((t) => t.arrange_order)) + 1, }; - return Object.assign({}, state, { - selectedTimetable: newTable, - timetables: [...state.timetables, newTable], - }); + return { ...state, selectedTimetable: newTable, timetables: [...state.timetables, newTable] }; } case ADD_LECTURE_TO_TIMETABLE: { if (!state.timetables || !state.selectedTimetable) { @@ -158,10 +141,7 @@ const timetable = (state = initialState, action: TimetableAction) => { lectures: state.selectedTimetable.lectures.concat([action.lecture]), }; const newTables = state.timetables.map((t) => (t.id === newTable.id ? newTable : t)); - return Object.assign({}, state, { - selectedTimetable: newTable, - timetables: newTables, - }); + return { ...state, selectedTimetable: newTable, timetables: newTables }; } case REMOVE_LECTURE_FROM_TIMETABLE: { if (!state.timetables || !state.selectedTimetable) { @@ -172,10 +152,7 @@ const timetable = (state = initialState, action: TimetableAction) => { lectures: state.selectedTimetable.lectures.filter((l) => l.id !== action.lecture.id), }; const newTables = state.timetables.map((t) => (t.id === newTable.id ? newTable : t)); - return Object.assign({}, state, { - selectedTimetable: newTable, - timetables: newTables, - }); + return { ...state, selectedTimetable: newTable, timetables: newTables }; } case REORDER_TIMETABLE: { if (!state.timetables || !state.selectedTimetable || state.selectedTimetable.id === MY) { @@ -216,20 +193,13 @@ const timetable = (state = initialState, action: TimetableAction) => { }); } case UPDATE_CELL_SIZE: { - return Object.assign({}, state, { - cellWidth: action.width, - cellHeight: action.height, - }); + return { ...state, cellWidth: action.width, cellHeight: action.height }; } case SET_IS_DRAGGING: { - return Object.assign({}, state, { - isDragging: action.isDragging, - }); + return { ...state, isDragging: action.isDragging }; } case SET_MOBILE_IS_TIMETABLE_TABS_OPEN: { - return Object.assign({}, state, { - isTimetableTabsOpenOnMobile: action.isTimetableTabsOpenOnMobile, - }); + return { ...state, isTimetableTabsOpenOnMobile: action.isTimetableTabsOpenOnMobile }; } default: { return state; From fdf99f6e73281d499348c02864c74a452b948016 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Tue, 16 Apr 2024 11:27:04 +0900 Subject: [PATCH 09/11] refactor: use spread syntax instead of Object.assign() --- src/reducers/timetable/timetable.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/reducers/timetable/timetable.ts b/src/reducers/timetable/timetable.ts index c79d2eb..37acaf5 100644 --- a/src/reducers/timetable/timetable.ts +++ b/src/reducers/timetable/timetable.ts @@ -187,10 +187,7 @@ const timetable = (state = initialState, action: TimetableAction) => { }); newTables.sort((t1, t2) => t1.arrange_order - t2.arrange_order); const updatedTable = newTables.find((t) => t.id === state.selectedTimetable?.id); - return Object.assign({}, state, { - timetables: newTables, - selectedTimetable: updatedTable, - }); + return { ...state, timetables: newTables, selectedTimetable: updatedTable }; } case UPDATE_CELL_SIZE: { return { ...state, cellWidth: action.width, cellHeight: action.height }; From 79b0b6347d2d455501ee7fae52215f953c25b1da Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Wed, 24 Apr 2024 16:01:28 +0900 Subject: [PATCH 10/11] fix: use spread syntax for deep copy --- src/reducers/timetable/timetable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reducers/timetable/timetable.ts b/src/reducers/timetable/timetable.ts index 37acaf5..d7acdb9 100644 --- a/src/reducers/timetable/timetable.ts +++ b/src/reducers/timetable/timetable.ts @@ -127,7 +127,7 @@ const timetable = (state = initialState, action: TimetableAction) => { } const newTable = { id: action.id, - lectures: action.timetable.lectures, + lectures: { ...action.timetable.lectures }, arrange_order: Math.max(...state.timetables.map((t) => t.arrange_order)) + 1, }; return { ...state, selectedTimetable: newTable, timetables: [...state.timetables, newTable] }; From 048b859ec6f2b3ef73826deb656b341d0242b419 Mon Sep 17 00:00:00 2001 From: Yumin Cho Date: Thu, 25 Apr 2024 20:21:28 +0900 Subject: [PATCH 11/11] fix: deep copy nested object --- src/reducers/timetable/list.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/reducers/timetable/list.ts b/src/reducers/timetable/list.ts index 4fec1c9..4c60473 100644 --- a/src/reducers/timetable/list.ts +++ b/src/reducers/timetable/list.ts @@ -97,6 +97,7 @@ const list = (state = initialState, action: LectureListAction) => { */ case CLEAR_SEARCH_LIST_LECTURES: { const newState = { ...state }; + newState.lists = { ...newState.lists }; newState.lists[LectureListCode.SEARCH].lectureGroups = null; return newState; } @@ -106,6 +107,7 @@ const list = (state = initialState, action: LectureListAction) => { const newLectures = [...lectures, action.lecture]; const newLectureGroups = groupLectures(newLectures); const newState = { ...state }; + newState.lists = { ...newState.lists }; newState.lists[LectureListCode.CART].lectureGroups = newLectureGroups; return newState; } @@ -115,6 +117,7 @@ const list = (state = initialState, action: LectureListAction) => { const newLectures = lectures.filter((l) => l.id !== action.lecture.id); const newLectureGroups = groupLectures(newLectures); const newState = { ...state }; + newState.lists = { ...newState.lists }; newState.lists[LectureListCode.CART].lectureGroups = newLectureGroups; return newState; }