Skip to content

Commit b84f400

Browse files
authored
fix: default snapshot and config not actually cloning (@Miodec) (monkeytypegame#6333)
!nuf Closes monkeytypegame#6280
1 parent b52391e commit b84f400

File tree

19 files changed

+161
-133
lines changed

19 files changed

+161
-133
lines changed

frontend/__tests__/utils/config.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { getDefaultConfig } from "../../src/ts/constants/default-config";
12
import { migrateConfig } from "../../src/ts/utils/config";
2-
import DefaultConfig from "../../src/ts/constants/default-config";
33
import {
44
PartialConfig,
55
ShowAverageSchema,
@@ -11,8 +11,8 @@ describe("config.ts", () => {
1111
const partialConfig = {} as PartialConfig;
1212

1313
const result = migrateConfig(partialConfig);
14-
expect(result).toEqual(expect.objectContaining(DefaultConfig));
15-
for (const [key, value] of Object.entries(DefaultConfig)) {
14+
expect(result).toEqual(expect.objectContaining(getDefaultConfig()));
15+
for (const [key, value] of Object.entries(getDefaultConfig())) {
1616
expect(result).toHaveProperty(key, value);
1717
}
1818
});
@@ -22,7 +22,7 @@ describe("config.ts", () => {
2222
} as PartialConfig;
2323

2424
const result = migrateConfig(partialConfig);
25-
expect(result).toEqual(expect.objectContaining(DefaultConfig));
25+
expect(result).toEqual(expect.objectContaining(getDefaultConfig()));
2626
expect(result).not.toHaveProperty("legacy");
2727
});
2828
it("should correctly merge properties of various types", () => {

frontend/__tests__/utils/format.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { getDefaultConfig } from "../../src/ts/constants/default-config";
12
import { Formatting } from "../../src/ts/utils/format";
2-
import DefaultConfig from "../../src/ts/constants/default-config";
33
import { Config } from "@monkeytype/contracts/schemas/configs";
44

55
describe("format.ts", () => {
@@ -274,6 +274,6 @@ describe("format.ts", () => {
274274
});
275275

276276
function getInstance(config?: Partial<Config>): Formatting {
277-
const target: Config = { ...DefaultConfig, ...config };
277+
const target: Config = { ...getDefaultConfig(), ...config };
278278
return new Formatting(target);
279279
}

frontend/src/ts/config.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
isConfigValueValid,
88
} from "./config-validation";
99
import * as ConfigEvent from "./observables/config-event";
10-
import DefaultConfig from "./constants/default-config";
1110
import { isAuthenticated } from "./firebase";
1211
import * as AnalyticsController from "./controllers/analytics-controller";
1312
import * as AccountButton from "./elements/account-button";
@@ -29,14 +28,15 @@ import { Language, LanguageSchema } from "@monkeytype/contracts/schemas/util";
2928
import { LocalStorageWithSchema } from "./utils/local-storage-with-schema";
3029
import { migrateConfig } from "./utils/config";
3130
import { roundTo1 } from "@monkeytype/util/numbers";
31+
import { getDefaultConfig } from "./constants/default-config";
3232

3333
const configLS = new LocalStorageWithSchema({
3434
key: "config",
3535
schema: ConfigSchemas.ConfigSchema,
36-
fallback: DefaultConfig,
36+
fallback: getDefaultConfig(),
3737
migrate: (value, _issues) => {
3838
if (!isObject(value)) {
39-
return DefaultConfig;
39+
return getDefaultConfig();
4040
}
4141
//todo maybe send a full config to db so that it removes legacy values
4242

@@ -47,7 +47,7 @@ const configLS = new LocalStorageWithSchema({
4747
let loadDone: (value?: unknown) => void;
4848

4949
const config = {
50-
...DefaultConfig,
50+
...getDefaultConfig(),
5151
};
5252

5353
let configToSend = {} as Config;
@@ -1095,7 +1095,7 @@ export function setTimeConfig(
10951095
time: ConfigSchemas.TimeConfig,
10961096
nosave?: boolean
10971097
): boolean {
1098-
time = isNaN(time) || time < 0 ? DefaultConfig.time : time;
1098+
time = isNaN(time) || time < 0 ? getDefaultConfig().time : time;
10991099
if (!isConfigValueValid("time", time, ConfigSchemas.TimeConfigSchema))
11001100
return false;
11011101

@@ -1168,7 +1168,7 @@ export function setWordCount(
11681168
nosave?: boolean
11691169
): boolean {
11701170
wordCount =
1171-
wordCount < 0 || wordCount > 100000 ? DefaultConfig.words : wordCount;
1171+
wordCount < 0 || wordCount > 100000 ? getDefaultConfig().words : wordCount;
11721172

11731173
if (!isConfigValueValid("words", wordCount, ConfigSchemas.WordCountSchema))
11741174
return false;
@@ -1382,7 +1382,7 @@ export function setAutoSwitchTheme(
13821382
return false;
13831383
}
13841384

1385-
boolean = boolean ?? DefaultConfig.autoSwitchTheme;
1385+
boolean = boolean ?? getDefaultConfig().autoSwitchTheme;
13861386
config.autoSwitchTheme = boolean;
13871387
saveToLocalStorage("autoSwitchTheme", nosave);
13881388
ConfigEvent.dispatch("autoSwitchTheme", config.autoSwitchTheme);
@@ -1985,9 +1985,9 @@ export async function apply(
19851985
ConfigEvent.dispatch("fullConfigChange");
19861986

19871987
const configObj = configToApply as Config;
1988-
(Object.keys(DefaultConfig) as (keyof Config)[]).forEach((configKey) => {
1988+
(Object.keys(getDefaultConfig()) as (keyof Config)[]).forEach((configKey) => {
19891989
if (configObj[configKey] === undefined) {
1990-
const newValue = DefaultConfig[configKey];
1990+
const newValue = getDefaultConfig()[configKey];
19911991
(configObj[configKey] as typeof newValue) = newValue;
19921992
}
19931993
});
@@ -2095,7 +2095,7 @@ export async function apply(
20952095
}
20962096

20972097
export async function reset(): Promise<void> {
2098-
await apply(DefaultConfig);
2098+
await apply(getDefaultConfig());
20992099
await DB.resetConfig();
21002100
saveFullConfigToLocalStorage(true);
21012101
}
@@ -2116,7 +2116,7 @@ export function getConfigChanges(): Partial<Config> {
21162116
const configChanges: Partial<Config> = {};
21172117
typedKeys(config)
21182118
.filter((key) => {
2119-
return config[key] !== DefaultConfig[key];
2119+
return config[key] !== getDefaultConfig()[key];
21202120
})
21212121
.forEach((key) => {
21222122
//@ts-expect-error this is fine

frontend/src/ts/constants/default-config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,6 @@ const obj = {
105105
maxLineWidth: 0,
106106
} as Config;
107107

108-
export default deepClone(obj);
108+
export function getDefaultConfig(): Config {
109+
return deepClone(obj);
110+
}

frontend/src/ts/constants/default-snapshot.ts

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,94 @@
1+
import {
2+
ResultFilters,
3+
User,
4+
UserProfileDetails,
5+
UserTag,
6+
} from "@monkeytype/contracts/schemas/users";
17
import { deepClone } from "../utils/misc";
2-
import defaultConfig from "./default-config";
8+
import { getDefaultConfig } from "./default-config";
9+
import { Mode } from "@monkeytype/contracts/schemas/shared";
10+
import { Result } from "@monkeytype/contracts/schemas/results";
11+
import { Config } from "@monkeytype/contracts/schemas/configs";
12+
import {
13+
ModifiableTestActivityCalendar,
14+
TestActivityCalendar,
15+
} from "../elements/test-activity-calendar";
16+
import { Preset } from "@monkeytype/contracts/schemas/presets";
17+
18+
export type SnapshotUserTag = UserTag & {
19+
active?: boolean;
20+
display: string;
21+
};
22+
23+
export type SnapshotResult<M extends Mode> = Omit<
24+
Result<M>,
25+
| "_id"
26+
| "bailedOut"
27+
| "blindMode"
28+
| "lazyMode"
29+
| "difficulty"
30+
| "funbox"
31+
| "language"
32+
| "numbers"
33+
| "punctuation"
34+
| "quoteLength"
35+
| "restartCount"
36+
| "incompleteTestSeconds"
37+
| "afkDuration"
38+
| "tags"
39+
> & {
40+
_id: string;
41+
bailedOut: boolean;
42+
blindMode: boolean;
43+
lazyMode: boolean;
44+
difficulty: string;
45+
funbox: string;
46+
language: string;
47+
numbers: boolean;
48+
punctuation: boolean;
49+
quoteLength: number;
50+
restartCount: number;
51+
incompleteTestSeconds: number;
52+
afkDuration: number;
53+
tags: string[];
54+
};
55+
56+
export type Snapshot = Omit<
57+
User,
58+
| "timeTyping"
59+
| "startedTests"
60+
| "completedTests"
61+
| "profileDetails"
62+
| "streak"
63+
| "resultFilterPresets"
64+
| "tags"
65+
| "xp"
66+
| "testActivity"
67+
> & {
68+
typingStats: {
69+
timeTyping: number;
70+
startedTests: number;
71+
completedTests: number;
72+
};
73+
details?: UserProfileDetails;
74+
inboxUnreadSize: number;
75+
streak: number;
76+
maxStreak: number;
77+
filterPresets: ResultFilters[];
78+
isPremium: boolean;
79+
streakHourOffset?: number;
80+
config: Config;
81+
tags: SnapshotUserTag[];
82+
presets: SnapshotPreset[];
83+
results?: SnapshotResult<Mode>[];
84+
xp: number;
85+
testActivity?: ModifiableTestActivityCalendar;
86+
testActivityByYear?: { [key: string]: TestActivityCalendar };
87+
};
88+
89+
export type SnapshotPreset = Preset & {
90+
display: string;
91+
};
392

493
const defaultSnap = {
594
results: undefined,
@@ -14,7 +103,7 @@ const defaultSnap = {
14103
email: "",
15104
uid: "",
16105
isPremium: false,
17-
config: defaultConfig,
106+
config: getDefaultConfig(),
18107
customThemes: [],
19108
presets: [],
20109
tags: [],
@@ -42,6 +131,8 @@ const defaultSnap = {
42131
60: { english: { count: 0, rank: 0 } },
43132
},
44133
},
45-
};
134+
} as Snapshot;
46135

47-
export default deepClone(defaultSnap);
136+
export function getDefaultSnapshot(): Snapshot {
137+
return deepClone(defaultSnap);
138+
}

frontend/src/ts/controllers/account-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import { FirebaseError } from "firebase/app";
4949
import * as PSA from "../elements/psa";
5050
import defaultResultFilters from "../constants/default-result-filters";
5151
import { getActiveFunboxesWithFunction } from "../test/funbox/list";
52+
import { Snapshot } from "../constants/default-snapshot";
5253

5354
export const gmailProvider = new GoogleAuthProvider();
5455
export const githubProvider = new GithubAuthProvider();
@@ -124,7 +125,7 @@ async function getDataAndInit(): Promise<boolean> {
124125
LoadingPage.updateBar(45);
125126
}
126127
LoadingPage.updateText("Applying settings...");
127-
const snapshot = DB.getSnapshot() as DB.Snapshot;
128+
const snapshot = DB.getSnapshot() as Snapshot;
128129
AccountButton.update(snapshot);
129130
Alerts.setNotificationBubbleVisible(snapshot.inboxUnreadSize > 0);
130131
showFavoriteQuoteLength();

frontend/src/ts/controllers/preset-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as Notifications from "../elements/notifications";
55
import * as TestLogic from "../test/test-logic";
66
import { migrateConfig, replaceLegacyValues } from "../utils/config";
77
import * as TagController from "./tag-controller";
8+
import { SnapshotPreset } from "../constants/default-snapshot";
89

910
export async function apply(_id: string): Promise<void> {
1011
const snapshot = DB.getSnapshot();
@@ -41,7 +42,7 @@ export async function apply(_id: string): Promise<void> {
4142
});
4243
UpdateConfig.saveFullConfigToLocalStorage();
4344
}
44-
function isPartialPreset(preset: DB.SnapshotPreset): boolean {
45+
function isPartialPreset(preset: SnapshotPreset): boolean {
4546
return preset.settingGroups !== undefined && preset.settingGroups !== null;
4647
}
4748

0 commit comments

Comments
 (0)