Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions frontend/__tests__/root/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import * as FunboxValidation from "../../src/ts/test/funbox/funbox-validation";
import * as ConfigValidation from "../../src/ts/config-validation";
import * as ConfigEvent from "../../src/ts/observables/config-event";
import * as DB from "../../src/ts/db";
import * as ApeConfig from "../../src/ts/ape/config";
import * as AccountButton from "../../src/ts/elements/account-button";
import * as Notifications from "../../src/ts/elements/notifications";
const { replaceConfig, getConfig } = Config.__testing;
Expand All @@ -31,7 +31,7 @@ describe("Config", () => {
"isConfigValueValid",
);
const dispatchConfigEventMock = vi.spyOn(ConfigEvent, "dispatch");
const dbSaveConfigMock = vi.spyOn(DB, "saveConfig");
const dbSaveConfigMock = vi.spyOn(ApeConfig, "saveConfig");
const accountButtonLoadingMock = vi.spyOn(AccountButton, "loading");
const notificationAddMock = vi.spyOn(Notifications, "add");
const miscReloadAfterMock = vi.spyOn(Misc, "reloadAfter");
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/ts/ape/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Config } from "@monkeytype/schemas/configs";
import { isAuthenticated } from "../firebase";
import * as Notifications from "../elements/notifications";
import Ape from ".";

export async function saveConfig(config: Partial<Config>): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.save({ body: config });
if (response.status !== 200) {
Notifications.add("Failed to save config", -1, { response });
}
}
}
export async function deleteConfig(): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.delete();
if (response.status !== 200) {
Notifications.add("Failed to reset config", -1, { response });
}
}
}
19 changes: 2 additions & 17 deletions frontend/src/ts/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "firebase/auth";

import Ape from "./ape";
import Config, { applyConfig, saveFullConfigToLocalStorage } from "./config";
import { updateFromServer as updateConfigFromServer } from "./config";
import { navigate } from "./controllers/route-controller";
import * as DB from "./db";
import * as Notifications from "./elements/notifications";
Expand All @@ -31,7 +31,6 @@ import * as Sentry from "./sentry";
import { showLoaderBar, hideLoaderBar } from "./signals/loader-bar";
import * as ConnectionState from "./states/connection";
import { addBanner } from "./stores/banners";
import { getActiveFunboxesWithFunction } from "./test/funbox/list";
import { qs, qsa } from "./utils/dom";
import * as Misc from "./utils/misc";

Expand Down Expand Up @@ -94,21 +93,7 @@ async function getDataAndInit(): Promise<boolean> {
});
}

const areConfigsEqual =
JSON.stringify(Config) === JSON.stringify(snapshot.config);

if (Config === undefined || !areConfigsEqual) {
console.log(
"no local config or local and db configs are different - applying db",
);
await applyConfig(snapshot.config);
saveFullConfigToLocalStorage(true);

//funboxes might be different and they wont activate on the account page
for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) {
fb.functions.applyGlobalCSS();
}
}
await updateConfigFromServer();
return true;
} catch (error) {
console.error(error);
Expand Down
51 changes: 47 additions & 4 deletions frontend/src/ts/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as DB from "./db";
import * as Notifications from "./elements/notifications";
import { isConfigValueValid } from "./config-validation";
import * as ConfigEvent from "./observables/config-event";
Expand All @@ -24,6 +23,9 @@ import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
import { ZodSchema } from "zod";
import * as TestState from "./test/test-state";
import { ConfigMetadataObject, configMetadata } from "./config-metadata";
import { deleteConfig, saveConfig } from "./ape/config";
import Ape from "./ape";
import { SnapshotInitError } from "./db";

const configLS = new LocalStorageWithSchema({
key: "config",
Expand All @@ -47,7 +49,7 @@ let configToSend: Partial<Config> = {};
const saveToDatabase = debounce(1000, () => {
if (Object.keys(configToSend).length > 0) {
AccountButton.loading(true);
void DB.saveConfig(configToSend).then(() => {
void saveConfig(configToSend).then(() => {
AccountButton.loading(false);
});
}
Expand All @@ -73,7 +75,7 @@ export function saveFullConfigToLocalStorage(noDbCheck = false): void {
configLS.set(config);
if (!noDbCheck) {
AccountButton.loading(true);
void DB.saveConfig(config);
void saveConfig(config);
AccountButton.loading(false);
}
}
Expand Down Expand Up @@ -297,7 +299,7 @@ export async function applyConfig(

export async function resetConfig(): Promise<void> {
await applyConfig(getDefaultConfig());
await DB.resetConfig();
await deleteConfig();
saveFullConfigToLocalStorage(true);
}

Expand Down Expand Up @@ -350,6 +352,47 @@ export async function applyConfigFromJson(json: string): Promise<void> {
}
}

export async function updateFromServer(): Promise<void> {
const remoteConfig = await getRemoteConfig();

const areConfigsEqual =
JSON.stringify(config) === JSON.stringify(remoteConfig);

if (config === undefined || !areConfigsEqual) {
console.log(
"no local config or local and db configs are different - applying db",
);
await applyConfig(remoteConfig);
saveFullConfigToLocalStorage(true);
}
}

async function getRemoteConfig(): Promise<ConfigSchemas.Config> {
const response = await Ape.configs.get();

if (response.status !== 200) {
throw new SnapshotInitError(
`${response.body.message} (config)`,
response.status,
);
}

const configData = response.body.data;
if (configData !== null && "config" in configData) {
throw new Error(
"Config data is not in the correct format. Please refresh the page or contact support.",
);
}

if (configData === undefined || configData === null) {
return {
...getDefaultConfig(),
};
} else {
return migrateConfig(configData);
}
}

const { promise: configLoadPromise, resolve: loadDone } =
promiseWithResolvers();

Expand Down
3 changes: 1 addition & 2 deletions frontend/src/ts/constants/default-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { getDefaultConfig } from "./default-config";
import { Mode } from "@monkeytype/schemas/shared";
import { Result } from "@monkeytype/schemas/results";
import { Config, Difficulty, FunboxName } from "@monkeytype/schemas/configs";
import { Difficulty, FunboxName } from "@monkeytype/schemas/configs";
import {
ModifiableTestActivityCalendar,
TestActivityCalendar,
Expand Down Expand Up @@ -78,7 +78,6 @@ export type Snapshot = Omit<
filterPresets: ResultFilters[];
isPremium: boolean;
streakHourOffset?: number;
config: Config;
tags: SnapshotUserTag[];
presets: SnapshotPreset[];
results?: SnapshotResult<Mode>[];
Expand Down
46 changes: 2 additions & 44 deletions frontend/src/ts/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import * as Notifications from "./elements/notifications";
import { isAuthenticated, getAuthenticatedUser } from "./firebase";
import * as ConnectionState from "./states/connection";
import { lastElementFromArray } from "./utils/arrays";
import { migrateConfig } from "./utils/config";
import * as Dates from "date-fns";
import {
TestActivityCalendar,
ModifiableTestActivityCalendar,
} from "./elements/test-activity-calendar";
import { showLoaderBar, hideLoaderBar } from "./signals/loader-bar";
import { Badge, CustomTheme } from "@monkeytype/schemas/users";
import { Config, Difficulty } from "@monkeytype/schemas/configs";
import { Difficulty } from "@monkeytype/schemas/configs";
import {
Mode,
Mode2,
Expand All @@ -25,7 +24,6 @@ import {
SnapshotResult,
SnapshotUserTag,
} from "./constants/default-snapshot";
import { getDefaultConfig } from "./constants/default-config";
import { FunboxMetadata } from "../../../packages/funbox/src/types";
import { getFirstDayOfTheWeek } from "./utils/date-and-time";
import { Language } from "@monkeytype/schemas/languages";
Expand Down Expand Up @@ -98,10 +96,9 @@ export async function initSnapshot(): Promise<Snapshot | false> {
? Ape.connections.get()
: { status: 200, body: { message: "", data: [] } };

const [userResponse, configResponse, presetsResponse, connectionsResponse] =
const [userResponse, presetsResponse, connectionsResponse] =
await Promise.all([
Ape.users.get(),
Ape.configs.get(),
Ape.presets.get(),
connectionsRequest,
]);
Expand All @@ -112,12 +109,6 @@ export async function initSnapshot(): Promise<Snapshot | false> {
userResponse.status,
);
}
if (configResponse.status !== 200) {
throw new SnapshotInitError(
`${configResponse.body.message} (config)`,
configResponse.status,
);
}
if (presetsResponse.status !== 200) {
throw new SnapshotInitError(
`${presetsResponse.body.message} (presets)`,
Expand All @@ -132,7 +123,6 @@ export async function initSnapshot(): Promise<Snapshot | false> {
}

const userData = userResponse.body.data;
const configData = configResponse.body.data;
const presetsData = presetsResponse.body.data;
const connectionsData = connectionsResponse.body.data;

Expand All @@ -143,12 +133,6 @@ export async function initSnapshot(): Promise<Snapshot | false> {
);
}

if (configData !== null && "config" in configData) {
throw new Error(
"Config data is not in the correct format. Please refresh the page or contact support.",
);
}

snap.name = userData.name;
snap.personalBests = userData.personalBests;
snap.personalBests ??= {
Expand Down Expand Up @@ -203,14 +187,6 @@ export async function initSnapshot(): Promise<Snapshot | false> {
snap.lbMemory = userData.lbMemory;
}

if (configData === undefined || configData === null) {
snap.config = {
...getDefaultConfig(),
};
} else {
snap.config = migrateConfig(configData);
}

snap.customThemes = userData.customThemes ?? [];

// const userDataTags: MonkeyTypes.UserTagWithDisplay[] = userData.tags ?? [];
Expand Down Expand Up @@ -976,24 +952,6 @@ export async function updateLbMemory<M extends Mode>(
}
}

export async function saveConfig(config: Partial<Config>): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.save({ body: config });
if (response.status !== 200) {
Notifications.add("Failed to save config", -1, { response });
}
}
}

export async function resetConfig(): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.delete();
if (response.status !== 200) {
Notifications.add("Failed to reset config", -1, { response });
}
}
}

export type SaveLocalResultData = {
xp?: number;
streak?: number;
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/ts/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as AccountButton from "./elements/account-button";
//@ts-expect-error no types for this package
import Konami from "konami";
import * as ServerConfiguration from "./ape/server-configuration";
import { getActiveFunboxesWithFunction } from "./test/funbox/list";
import { configLoadPromise } from "./config";
import { authPromise } from "./firebase";
import { animate } from "animejs";
Expand All @@ -23,10 +22,6 @@ onDOMReady(async () => {
});
MerchBanner.showIfNotClosedBefore();

for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) {
fb.functions.applyGlobalCSS();
}

const app = document.querySelector("#app") as HTMLElement;
app?.classList.remove("hidden");
animate(app, {
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/ts/test/funbox/funbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { HighlightMode, FunboxName } from "@monkeytype/schemas/configs";
import { Mode } from "@monkeytype/schemas/shared";
import { checkCompatibility } from "@monkeytype/funbox";
import {
getAllFunboxes,
getActiveFunboxes,
getActiveFunboxNames,
get,
Expand All @@ -23,6 +24,7 @@ import {
import { checkForcedConfig } from "./funbox-validation";
import { tryCatch } from "@monkeytype/util/trycatch";
import { qs } from "../../utils/dom";
import * as ConfigEvent from "../../observables/config-event";

export function toggleScript(...params: string[]): void {
if (Config.funbox.length === 0) return;
Expand Down Expand Up @@ -252,3 +254,16 @@ async function applyFunboxCSS(): Promise<boolean> {
}
return true;
}

ConfigEvent.subscribe(async ({ key }) => {
if (key === "funbox") {
const active = getActiveFunboxNames();
getAllFunboxes()
.filter((it) => !active.includes(it.name))
.forEach((it) => it.functions?.clearGlobal?.());

for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) {
fb.functions.applyGlobalCSS();
}
}
});
Loading