Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions website/src/actions/__snapshots__/settings.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,9 @@ exports[`settings should dispatch a select of a semester value 1`] = `
}
`;

exports[`settings should dispatch a selection of a mode 1`] = `
exports[`settings should dispatch a selection of a color scheme preference 1`] = `
{
"payload": "LIGHT",
"type": "SELECT_MODE",
}
`;

exports[`settings should dispatch a toggle of a mode 1`] = `
{
"payload": null,
"type": "TOGGLE_MODE",
"payload": "LIGHT_COLOR_SCHEME_PREFERENCE",
"type": "SELECT_COLOR_SCHEME",
}
`;
12 changes: 4 additions & 8 deletions website/src/actions/settings.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Faculty, Semester } from 'types/modules';
import { LIGHT_COLOR_SCHEME_PREFERENCE } from 'types/settings';

import * as actions from 'actions/settings';

import { LIGHT_MODE } from 'types/settings';

describe('settings', () => {
test('should dispatch a select of a semester value', () => {
const semester: Semester = 1;
Expand All @@ -15,12 +14,9 @@ describe('settings', () => {
expect(actions.selectNewStudent(newStudent)).toMatchSnapshot();
});

test('should dispatch a selection of a mode', () => {
expect(actions.selectMode(LIGHT_MODE)).toMatchSnapshot();
});

test('should dispatch a toggle of a mode', () => {
expect(actions.toggleMode()).toMatchSnapshot();
test('should dispatch a selection of a color scheme preference', () => {
const colorSchemePreference = LIGHT_COLOR_SCHEME_PREFERENCE;
expect(actions.selectColorScheme(colorSchemePreference)).toMatchSnapshot();
});

test('should dispatch a select of a faculty value', () => {
Expand Down
16 changes: 4 additions & 12 deletions website/src/actions/settings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Faculty, Semester } from 'types/modules';
import { Mode } from 'types/settings';
import { ColorSchemePreference } from 'types/settings';
import { ModuleTableOrder } from 'types/reducers';

import { RegPeriod, ScheduleType } from 'config';
Expand Down Expand Up @@ -29,22 +29,14 @@ export function selectFaculty(faculty: Faculty) {
};
}

export const SELECT_MODE = 'SELECT_MODE' as const;
export function selectMode(mode: Mode) {
export const SELECT_COLOR_SCHEME = 'SELECT_COLOR_SCHEME' as const;
export function selectColorScheme(mode: ColorSchemePreference) {
return {
type: SELECT_MODE,
type: SELECT_COLOR_SCHEME,
payload: mode,
};
}

export const TOGGLE_MODE = 'TOGGLE_MODE' as const;
export function toggleMode() {
return {
type: TOGGLE_MODE,
payload: null,
};
}

export const DISMISS_MODREG_NOTIFICATION = 'DISMISS_MODREG_NOTIFICATION' as const;
export function dismissModregNotification(round: RegPeriod) {
return {
Expand Down
22 changes: 17 additions & 5 deletions website/src/apis/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Semester } from 'types/modules';
import { extractStateForExport } from 'utils/export';
import { State } from 'types/state';
import { SemTimetableConfig } from 'types/timetables';
import { ColorScheme } from 'types/settings';

export type ExportOptions = {
pixelRatio?: number;
Expand All @@ -14,18 +15,29 @@ const baseUrl = 'https://export.nusmods.com/api/export';
function serializeState(
semester: Semester,
timetable: SemTimetableConfig,
colorScheme: ColorScheme,
state: State,
options: ExportOptions = {},
) {
return qs.stringify({
data: JSON.stringify(extractStateForExport(semester, timetable, state)),
data: JSON.stringify(extractStateForExport(semester, timetable, colorScheme, state)),
...options,
});
}

export default {
image: (semester: Semester, timetable: SemTimetableConfig, state: State, pixelRatio = 1) =>
`${baseUrl}/image?${serializeState(semester, timetable, state, { pixelRatio })}`,
pdf: (semester: Semester, timetable: SemTimetableConfig, state: State) =>
`${baseUrl}/pdf?${serializeState(semester, timetable, state)}`,
image: (
semester: Semester,
timetable: SemTimetableConfig,
colorScheme: ColorScheme,
state: State,
pixelRatio = 1,
) =>
`${baseUrl}/image?${serializeState(semester, timetable, colorScheme, state, { pixelRatio })}`,
pdf: (
semester: Semester,
timetable: SemTimetableConfig,
colorScheme: ColorScheme,
state: State,
) => `${baseUrl}/pdf?${serializeState(semester, timetable, colorScheme, state)}`,
};
4 changes: 2 additions & 2 deletions website/src/entry/export/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ExportData } from 'types/export';

import configureStore from 'bootstrapping/configure-store';
import { setExportedData } from 'actions/export';
import { DARK_MODE } from 'types/settings';
import { DARK_COLOR_SCHEME } from 'types/settings';
import { State as StoreState } from 'types/state';

import TimetableOnly from './TimetableOnly';
Expand All @@ -32,7 +32,7 @@ window.setData = function setData(modules, data, callback) {
const { semester, timetable, colors } = data;

if (document.body) {
document.body.classList.toggle('mode-dark', data.settings.mode === DARK_MODE);
document.body.classList.toggle('mode-dark', data.settings.colorScheme === DARK_COLOR_SCHEME);
}

store.dispatch(setExportedData(modules, data));
Expand Down
5 changes: 3 additions & 2 deletions website/src/reducers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { VERTICAL } from 'types/reducers';
import reducers from 'reducers';
import { setExportedData } from 'actions/export';
import modules from '__mocks__/modules/index';
import { DARK_COLOR_SCHEME, DARK_COLOR_SCHEME_PREFERENCE } from 'types/settings';

/* eslint-disable no-useless-computed-key */

Expand Down Expand Up @@ -34,7 +35,7 @@ const exportData: ExportData = {
showTitle: true,
},
settings: {
mode: 'DARK',
colorScheme: DARK_COLOR_SCHEME,
},
};

Expand Down Expand Up @@ -78,7 +79,7 @@ test('reducers should set export data state', () => {
});

expect(state.settings).toMatchObject({
mode: 'DARK',
colorScheme: DARK_COLOR_SCHEME_PREFERENCE,
});

expect(state.theme).toEqual({
Expand Down
38 changes: 22 additions & 16 deletions website/src/reducers/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { SettingsState } from 'types/reducers';

import * as actions from 'actions/settings';
import reducer from 'reducers/settings';
import { DARK_MODE, LIGHT_MODE } from 'types/settings';
import {
DARK_COLOR_SCHEME_PREFERENCE,
LIGHT_COLOR_SCHEME_PREFERENCE,
SYSTEM_COLOR_SCHEME_PREFERENCE,
} from 'types/settings';
import { initAction, rehydrateAction } from 'test-utils/redux';
import config, { RegPeriod } from 'config';

const initialState: SettingsState = {
newStudent: false,
faculty: '',
mode: LIGHT_MODE,
colorScheme: SYSTEM_COLOR_SCHEME_PREFERENCE,
hiddenInTimetable: [],
modRegNotification: {
enabled: true,
Expand All @@ -25,7 +29,14 @@ const initialState: SettingsState = {
const settingsWithNewStudent: SettingsState = { ...initialState, newStudent: true };
const faculty = 'School of Computing';
const settingsWithFaculty: SettingsState = { ...initialState, faculty };
const settingsWithDarkMode: SettingsState = { ...initialState, mode: DARK_MODE };
const settingsWithLightMode: SettingsState = {
...initialState,
colorScheme: LIGHT_COLOR_SCHEME_PREFERENCE,
};
const settingsWithDarkMode: SettingsState = {
...initialState,
colorScheme: DARK_COLOR_SCHEME_PREFERENCE,
};
const settingsWithDismissedNotifications: SettingsState = produce(initialState, (draft) => {
draft.modRegNotification.dismissed = [
{ type: 'Select Courses', name: '1' },
Expand All @@ -52,23 +63,18 @@ describe('settings', () => {
expect(nextState).toEqual(settingsWithFaculty);
});

test('can select mode', () => {
const action = actions.selectMode(DARK_MODE);
test('can select color scheme', () => {
const action = actions.selectColorScheme(DARK_COLOR_SCHEME_PREFERENCE);
const nextState: SettingsState = reducer(initialState, action);
expect(nextState).toEqual(settingsWithDarkMode);

const action2 = actions.selectMode(LIGHT_MODE);
const nextState2: SettingsState = reducer(nextState, action2);
expect(nextState2).toEqual(initialState);
});

test('can toggle mode', () => {
const action = actions.toggleMode();
const nextState: SettingsState = reducer(initialState, action);
expect(nextState).toEqual(settingsWithDarkMode);
const action2 = actions.selectColorScheme(LIGHT_COLOR_SCHEME_PREFERENCE);
const nextState2: SettingsState = reducer(initialState, action2);
expect(nextState2).toEqual(settingsWithLightMode);

const nextState2: SettingsState = reducer(nextState, action);
expect(nextState2).toEqual(initialState);
const action3 = actions.selectColorScheme(SYSTEM_COLOR_SCHEME_PREFERENCE);
const nextState3: SettingsState = reducer(nextState, action3);
expect(nextState3).toEqual(initialState);
});

test('set module table order', () => {
Expand Down
25 changes: 11 additions & 14 deletions website/src/reducers/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import {
DISMISS_MODREG_NOTIFICATION,
ENABLE_MODREG_NOTIFICATION,
SELECT_FACULTY,
SELECT_MODE,
SELECT_COLOR_SCHEME,
SELECT_NEW_STUDENT,
SET_LOAD_DISQUS_MANUALLY,
SET_MODULE_TABLE_SORT,
TOGGLE_BETA_TESTING_STATUS,
TOGGLE_MODREG_NOTIFICATION_GLOBALLY,
TOGGLE_MODE,
SET_MODREG_SCHEDULE_TYPE,
} from 'actions/settings';
import { SET_EXPORTED_DATA } from 'actions/constants';
import { DIMENSIONS, withTracker } from 'bootstrapping/matomo';
import { DARK_MODE, LIGHT_MODE } from 'types/settings';
import { SYSTEM_COLOR_SCHEME_PREFERENCE } from 'types/settings';
import config from 'config';
import { isRoundDismissed } from 'selectors/modreg';
import { colorSchemeToPreference } from 'utils/colorScheme';

export const defaultModRegNotificationState = {
semesterKey: config.getSemesterKey(),
Expand All @@ -34,7 +34,7 @@ export const defaultModRegNotificationState = {
const defaultSettingsState: SettingsState = {
newStudent: false,
faculty: '',
mode: LIGHT_MODE,
colorScheme: SYSTEM_COLOR_SCHEME_PREFERENCE,
hiddenInTimetable: [],
modRegNotification: defaultModRegNotificationState,
moduleTableOrder: 'exam',
Expand All @@ -54,17 +54,11 @@ function settings(state: SettingsState = defaultSettingsState, action: Actions):
...state,
faculty: action.payload,
};
case SELECT_MODE:
case SELECT_COLOR_SCHEME:
return {
...state,
mode: action.payload,
colorScheme: action.payload,
};
case TOGGLE_MODE:
return {
...state,
mode: state.mode === LIGHT_MODE ? DARK_MODE : LIGHT_MODE,
};

case TOGGLE_MODREG_NOTIFICATION_GLOBALLY:
return produce(state, (draft) => {
draft.modRegNotification.enabled = action.payload.enabled;
Expand All @@ -89,11 +83,14 @@ function settings(state: SettingsState = defaultSettingsState, action: Actions):
draft.modRegNotification.scheduleType = action.payload;
});

case SET_EXPORTED_DATA:
case SET_EXPORTED_DATA: {
const { colorScheme, ...otherSettings } = action.payload.settings;
return {
...state,
...action.payload.settings,
...otherSettings,
colorScheme: colorSchemeToPreference(action.payload.settings.colorScheme),
};
}

case SET_MODULE_TABLE_SORT:
return {
Expand Down
4 changes: 2 additions & 2 deletions website/src/types/export.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SemTimetableConfig } from 'types/timetables';
import { Semester, ModuleCode } from 'types/modules';
import { Mode } from 'types/settings';
import type { ColorScheme } from 'types/settings';
import { ColorMapping, ThemeState } from 'types/reducers';

export type ExportData = {
Expand All @@ -10,6 +10,6 @@ export type ExportData = {
readonly hidden: ModuleCode[];
readonly theme: ThemeState;
readonly settings: {
mode: Mode;
colorScheme: ColorScheme;
};
};
4 changes: 2 additions & 2 deletions website/src/types/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AxiosError } from 'axios';
import { RegPeriodType, ScheduleType } from 'config';

import { Mode } from './settings';
import { ColorSchemePreference } from './settings';
import { ColorIndex, Lesson, TimetableConfig } from './timetables';
import {
Faculty,
Expand Down Expand Up @@ -98,7 +98,7 @@ export type ModuleTableOrder = 'exam' | 'mc' | 'code';
export type SettingsState = {
readonly newStudent: boolean;
readonly faculty: Faculty | null;
readonly mode: Mode;
readonly colorScheme: ColorSchemePreference;
readonly hiddenInTimetable: ModuleCode[];
readonly modRegNotification: ModRegNotificationSettings;
readonly moduleTableOrder: ModuleTableOrder;
Expand Down
25 changes: 22 additions & 3 deletions website/src/types/settings.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
export type ThemeId = string;

/**
* Themes are the "color palette" of the website. They define the set of colors
* being used across the website.
*/
export type Theme = {
readonly id: ThemeId;
readonly name: string;
};

export type Mode = 'LIGHT' | 'DARK';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow this is a much-needed refactoring LOL good stuff

export const LIGHT_MODE: Mode = 'LIGHT';
export const DARK_MODE: Mode = 'DARK';
/**
* Color schemes simply define whether the website is in light or dark mode. This
* is not to be confused with themese, which define the color palette of the website.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: themese -> themes

*/
export type ColorScheme = 'LIGHT_COLOR_SCHEME' | 'DARK_COLOR_SCHEME';

export const LIGHT_COLOR_SCHEME: ColorScheme = 'LIGHT_COLOR_SCHEME';
export const DARK_COLOR_SCHEME: ColorScheme = 'DARK_COLOR_SCHEME';

export type ColorSchemePreference =
| 'SYSTEM_COLOR_SCHEME_PREFERENCE'
| 'LIGHT_COLOR_SCHEME_PREFERENCE'
| 'DARK_COLOR_SCHEME_PREFERENCE';

export const SYSTEM_COLOR_SCHEME_PREFERENCE: ColorSchemePreference =
'SYSTEM_COLOR_SCHEME_PREFERENCE';
export const LIGHT_COLOR_SCHEME_PREFERENCE: ColorSchemePreference = 'LIGHT_COLOR_SCHEME_PREFERENCE';
export const DARK_COLOR_SCHEME_PREFERENCE: ColorSchemePreference = 'DARK_COLOR_SCHEME_PREFERENCE';
Loading