diff options
author | Jack <[email protected]> | 2024-09-23 15:34:16 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-09-23 15:34:16 +0200 |
commit | b6bd5ba2b4d2d441c4e09ceec2ad42b24e10d097 (patch) | |
tree | 8b889f95b55682df6ca78b6a7abaf76120e87a51 | |
parent | d9788a15e7a63060bcb6e9fb5e860da771730b58 (diff) | |
download | monkeytype-b6bd5ba2b4d2d441c4e09ceec2ad42b24e10d097.tar.gz monkeytype-b6bd5ba2b4d2d441c4e09ceec2ad42b24e10d097.zip |
refactor: remove global type namespaces (@miodec) (#5907)
Remove global types, move types to where they originate from, import
them when needed.
198 files changed, 1608 insertions, 1642 deletions
diff --git a/backend/__migration__/testActivity.ts b/backend/__migration__/testActivity.ts index 655611f62..211d64d63 100644 --- a/backend/__migration__/testActivity.ts +++ b/backend/__migration__/testActivity.ts @@ -3,13 +3,15 @@ import * as DB from "../src/init/db"; import { Collection, Db } from "mongodb"; import readlineSync from "readline-sync"; +import { DBUser } from "../src/dal/user"; +import { DBResult } from "../src/utils/result"; const batchSize = 50; let appRunning = true; let db: Db | undefined; -let userCollection: Collection<MonkeyTypes.DBUser>; -let resultCollection: Collection<MonkeyTypes.DBResult>; +let userCollection: Collection<DBUser>; +let resultCollection: Collection<DBResult>; const filter = { testActivity: { $exists: false } }; diff --git a/backend/__tests__/__migration__/testActivity.spec.ts b/backend/__tests__/__migration__/testActivity.spec.ts index badc6f7f0..2b65382fb 100644 --- a/backend/__tests__/__migration__/testActivity.spec.ts +++ b/backend/__tests__/__migration__/testActivity.spec.ts @@ -2,6 +2,7 @@ import * as Migration from "../../__migration__/testActivity"; import * as UserTestData from "../__testData__/users"; import * as UserDal from "../../src/dal/user"; import * as ResultDal from "../../src/dal/result"; +import { DBResult } from "../../src/utils/result"; describe("testActivity migration", () => { it("migrates users without results", async () => { @@ -69,5 +70,5 @@ async function createResult(uid: string, timestamp: number): Promise<void> { keyConsistency: 0, chartData: "toolong", name: "", - } as unknown as ResultDal.DBResult); + } as unknown as DBResult); } diff --git a/backend/__tests__/__testData__/auth.ts b/backend/__tests__/__testData__/auth.ts index a1c0f94de..bd836d580 100644 --- a/backend/__tests__/__testData__/auth.ts +++ b/backend/__tests__/__testData__/auth.ts @@ -16,7 +16,7 @@ export async function mockAuthenticateWithApeKey( const apiKey = randomBytes(apeKeyBytes).toString("base64url"); const saltyHash = await hash(apiKey, apeKeySaltRounds); - const apeKey: MonkeyTypes.ApeKeyDB = { + const apeKey: ApeKeyDal.DBApeKey = { _id: new ObjectId(), name: "bob", enabled: true, diff --git a/backend/__tests__/__testData__/users.ts b/backend/__tests__/__testData__/users.ts index 21156d902..26c27b5b6 100644 --- a/backend/__tests__/__testData__/users.ts +++ b/backend/__tests__/__testData__/users.ts @@ -3,8 +3,8 @@ import * as UserDAL from "../../src/dal/user"; import { ObjectId } from "mongodb"; export async function createUser( - user?: Partial<MonkeyTypes.DBUser> -): Promise<MonkeyTypes.DBUser> { + user?: Partial<UserDAL.DBUser> +): Promise<UserDAL.DBUser> { const uid = new ObjectId().toHexString(); await UserDAL.addUser("user" + uid, uid + "@example.com", uid); await DB.collection("users").updateOne({ uid }, { $set: { ...user } }); @@ -12,8 +12,8 @@ export async function createUser( } export async function createUserWithoutMigration( - user?: Partial<MonkeyTypes.DBUser> -): Promise<MonkeyTypes.DBUser> { + user?: Partial<UserDAL.DBUser> +): Promise<UserDAL.DBUser> { const uid = new ObjectId().toHexString(); await UserDAL.addUser("user" + uid, uid + "@example.com", uid); await DB.collection("users").updateOne({ uid }, { $set: { ...user } }); diff --git a/backend/__tests__/api/controllers/admin.spec.ts b/backend/__tests__/api/controllers/admin.spec.ts index 7c362506f..dfe81861e 100644 --- a/backend/__tests__/api/controllers/admin.spec.ts +++ b/backend/__tests__/api/controllers/admin.spec.ts @@ -207,11 +207,11 @@ describe("AdminController", () => { const reportOne = { id: "1", reason: "one", - } as any as MonkeyTypes.Report; + } as any as ReportDal.DBReport; const reportTwo = { id: "2", reason: "two", - } as any as MonkeyTypes.Report; + } as any as ReportDal.DBReport; getReportsMock.mockResolvedValue([reportOne, reportTwo]); //WHEN @@ -321,11 +321,11 @@ describe("AdminController", () => { const reportOne = { id: "1", reason: "one", - } as any as MonkeyTypes.Report; + } as any as ReportDal.DBReport; const reportTwo = { id: "2", reason: "two", - } as any as MonkeyTypes.Report; + } as any as ReportDal.DBReport; getReportsMock.mockResolvedValue([reportOne, reportTwo]); //WHEN diff --git a/backend/__tests__/api/controllers/ape-key.spec.ts b/backend/__tests__/api/controllers/ape-key.spec.ts index 6061a4e99..7c05fbf2b 100644 --- a/backend/__tests__/api/controllers/ape-key.spec.ts +++ b/backend/__tests__/api/controllers/ape-key.spec.ts @@ -337,8 +337,8 @@ describe("ApeKeyController", () => { function apeKeyDb( uid: string, - data?: Partial<MonkeyTypes.ApeKeyDB> -): MonkeyTypes.ApeKeyDB { + data?: Partial<ApeKeyDal.DBApeKey> +): ApeKeyDal.DBApeKey { return { _id: new ObjectId(), uid, @@ -363,12 +363,9 @@ async function enableApeKeysEndpoints(enabled: boolean): Promise<void> { ); } -function user( - uid: string, - data: Partial<MonkeyTypes.DBUser> -): MonkeyTypes.DBUser { +function user(uid: string, data: Partial<UserDal.DBUser>): UserDal.DBUser { return { uid, ...data, - } as MonkeyTypes.DBUser; + } as UserDal.DBUser; } diff --git a/backend/__tests__/api/controllers/result.spec.ts b/backend/__tests__/api/controllers/result.spec.ts index 07981f614..bffa1e538 100644 --- a/backend/__tests__/api/controllers/result.spec.ts +++ b/backend/__tests__/api/controllers/result.spec.ts @@ -10,6 +10,7 @@ import { DecodedIdToken } from "firebase-admin/lib/auth/token-verifier"; import { ObjectId } from "mongodb"; import { mockAuthenticateWithApeKey } from "../../__testData__/auth"; import { enableRateLimitExpects } from "../../__testData__/rate-limit"; +import { DBResult } from "../../../src/utils/result"; const uid = "123456"; const mockDecodedToken: DecodedIdToken = { @@ -830,10 +831,7 @@ async function enablePremiumFeatures(premium: boolean): Promise<void> { mockConfig ); } -function givenDbResult( - uid: string, - customize?: Partial<MonkeyTypes.DBResult> -): MonkeyTypes.DBResult { +function givenDbResult(uid: string, customize?: Partial<DBResult>): DBResult { return { _id: new ObjectId(), wpm: Math.random() * 100, diff --git a/backend/__tests__/api/controllers/user.spec.ts b/backend/__tests__/api/controllers/user.spec.ts index 4e9cc9730..746d8696f 100644 --- a/backend/__tests__/api/controllers/user.spec.ts +++ b/backend/__tests__/api/controllers/user.spec.ts @@ -484,7 +484,7 @@ describe("user controller test", () => { //given getUserMock.mockResolvedValue({ testActivity: { "2023": [1, 2, 3], "2024": [4, 5, 6] }, - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser); + } as Partial<UserDal.DBUser> as UserDal.DBUser); //when await mockApp @@ -497,7 +497,7 @@ describe("user controller test", () => { //given getUserMock.mockResolvedValue({ testActivity: { "2023": [1, 2, 3], "2024": [4, 5, 6] }, - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser); + } as Partial<UserDal.DBUser> as UserDal.DBUser); vi.spyOn(UserDal, "checkIfUserIsPremium").mockResolvedValue(true); await enablePremiumFeatures(true); @@ -633,7 +633,7 @@ describe("user controller test", () => { email: "email", discordId: "discordId", banned: true, - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser; + } as Partial<UserDal.DBUser> as UserDal.DBUser; await getUserMock.mockResolvedValue(user); //WHEN @@ -664,7 +664,7 @@ describe("user controller test", () => { name: "name", email: "email", discordId: "discordId", - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser; + } as Partial<UserDal.DBUser> as UserDal.DBUser; getUserMock.mockResolvedValue(user); //WHEN @@ -1555,7 +1555,7 @@ describe("user controller test", () => { uid, name: "name", email: "email", - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser; + } as Partial<UserDal.DBUser> as UserDal.DBUser; getUserMock.mockResolvedValue(user); blocklistContainsMock.mockResolvedValue(true); @@ -2061,12 +2061,12 @@ describe("user controller test", () => { it("should get tags", async () => { //GIVEN - const tagOne: MonkeyTypes.DBUserTag = { + const tagOne: UserDal.DBUserTag = { _id: new ObjectId(), name: "tagOne", personalBests: {} as any, }; - const tagTwo: MonkeyTypes.DBUserTag = { + const tagTwo: UserDal.DBUserTag = { _id: new ObjectId(), name: "tagOne", personalBests: {} as any, @@ -2171,12 +2171,12 @@ describe("user controller test", () => { }); it("should get custom themes", async () => { //GIVEN - const themeOne: MonkeyTypes.DBCustomTheme = { + const themeOne: UserDal.DBCustomTheme = { _id: new ObjectId(), name: "themeOne", colors: new Array(10).fill("#000000") as any, }; - const themeTwo: MonkeyTypes.DBCustomTheme = { + const themeTwo: UserDal.DBCustomTheme = { _id: new ObjectId(), name: "themeTwo", colors: new Array(10).fill("#FFFFFF") as any, @@ -2207,7 +2207,7 @@ describe("user controller test", () => { it("should add ", async () => { //GIVEN - const addedTheme: MonkeyTypes.DBCustomTheme = { + const addedTheme: UserDal.DBCustomTheme = { _id: new ObjectId(), name: "custom", colors: new Array(10).fill("#000000") as any, @@ -2501,7 +2501,7 @@ describe("user controller test", () => { it("should get stats", async () => { //GIVEN const stats: Pick< - MonkeyTypes.DBUser, + UserDal.DBUser, "startedTests" | "completedTests" | "timeTyping" > = { startedTests: 5, @@ -2672,7 +2672,7 @@ describe("user controller test", () => { const checkIfUserIsPremiumMock = vi.spyOn(UserDal, "checkIfUserIsPremium"); const leaderboardGetRankMock = vi.spyOn(LeaderboardDal, "getRank"); - const foundUser: Partial<MonkeyTypes.DBUser> = { + const foundUser: Partial<UserDal.DBUser> = { _id: new ObjectId(), uid: new ObjectId().toHexString(), name: "bob", @@ -3548,7 +3548,7 @@ describe("user controller test", () => { testActivity: { "2024": fillYearWithDay(94), }, - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser; + } as Partial<UserDal.DBUser> as UserDal.DBUser; getUserMock.mockResolvedValue(user); //WHEN @@ -3584,7 +3584,7 @@ describe("user controller test", () => { maxLength: 1024, hourOffset: 2, }, - } as Partial<MonkeyTypes.DBUser> as MonkeyTypes.DBUser; + } as Partial<UserDal.DBUser> as UserDal.DBUser; getUserMock.mockResolvedValue(user); //WHEN diff --git a/backend/__tests__/dal/leaderboards.spec.ts b/backend/__tests__/dal/leaderboards.spec.ts index 1c3925b3a..160625049 100644 --- a/backend/__tests__/dal/leaderboards.spec.ts +++ b/backend/__tests__/dal/leaderboards.spec.ts @@ -9,6 +9,7 @@ import type { PersonalBest } from "@monkeytype/contracts/schemas/shared"; const configuration = Configuration.getCachedConfiguration(); import * as DB from "../../src/init/db"; +import { LbPersonalBests } from "../../src/utils/pb"; describe("LeaderboardsDal", () => { describe("update", () => { @@ -289,14 +290,14 @@ function expectedLbEntry( } async function createUser( - lbPersonalBests?: MonkeyTypes.LbPersonalBests, - userProperties?: Partial<MonkeyTypes.DBUser> -): Promise<MonkeyTypes.DBUser> { + lbPersonalBests?: LbPersonalBests, + userProperties?: Partial<UserDal.DBUser> +): Promise<UserDal.DBUser> { const uid = new ObjectId().toHexString(); await UserDal.addUser("User " + uid, uid + "@example.com", uid); await DB.getDb() - ?.collection<MonkeyTypes.DBUser>("users") + ?.collection<UserDal.DBUser>("users") .updateOne( { uid }, { @@ -313,11 +314,8 @@ async function createUser( return await UserDal.getUser(uid, "test"); } -function lbBests( - pb15?: PersonalBest, - pb60?: PersonalBest -): MonkeyTypes.LbPersonalBests { - const result: MonkeyTypes.LbPersonalBests = { time: {} }; +function lbBests(pb15?: PersonalBest, pb60?: PersonalBest): LbPersonalBests { + const result: LbPersonalBests = { time: {} }; if (pb15) result.time["15"] = { english: pb15 }; if (pb60) result.time["60"] = { english: pb60 }; return result; @@ -355,7 +353,7 @@ function premium(expirationDeltaSeconds: number) { interface ExpectedLbEntry { rank: number; - user: MonkeyTypes.DBUser; + user: UserDal.DBUser; badgeId?: number; isPremium?: boolean; } diff --git a/backend/__tests__/dal/result.spec.ts b/backend/__tests__/dal/result.spec.ts index e37d16f43..a16eab6f4 100644 --- a/backend/__tests__/dal/result.spec.ts +++ b/backend/__tests__/dal/result.spec.ts @@ -1,6 +1,7 @@ import * as ResultDal from "../../src/dal/result"; import { ObjectId } from "mongodb"; import * as UserDal from "../../src/dal/user"; +import { DBResult } from "../../src/utils/result"; let uid: string = ""; const timestamp = Date.now() - 60000; @@ -11,7 +12,7 @@ async function createDummyData( timestamp: number, tag?: string ): Promise<void> { - const dummyUser: MonkeyTypes.DBUser = { + const dummyUser: UserDal.DBUser = { _id: new ObjectId(), uid, addedAt: 0, @@ -56,7 +57,7 @@ async function createDummyData( language: "english", isPb: false, name: "Test", - } as MonkeyTypes.DBResult); + } as DBResult); } } describe("ResultDal", () => { diff --git a/backend/__tests__/dal/user.spec.ts b/backend/__tests__/dal/user.spec.ts index e8feb2de1..6df733608 100644 --- a/backend/__tests__/dal/user.spec.ts +++ b/backend/__tests__/dal/user.spec.ts @@ -493,7 +493,7 @@ describe("UserDal", () => { it("should fail if tag not found", async () => { // given - const tagOne: MonkeyTypes.DBUserTag = { + const tagOne: UserDAL.DBUserTag = { _id: new ObjectId(), name: "one", personalBests: {} as any, @@ -510,7 +510,7 @@ describe("UserDal", () => { it("editTag success", async () => { // given - const tagOne: MonkeyTypes.DBUserTag = { + const tagOne: UserDAL.DBUserTag = { _id: new ObjectId(), name: "one", personalBests: {} as any, @@ -540,7 +540,7 @@ describe("UserDal", () => { it("should return error if tag is unknown", async () => { // given - const tagOne: MonkeyTypes.DBUserTag = { + const tagOne: UserDAL.DBUserTag = { _id: new ObjectId(), name: "one", personalBests: {} as any, @@ -594,7 +594,7 @@ describe("UserDal", () => { it("should return error if tag is unknown", async () => { // given - const tagOne: MonkeyTypes.DBUserTag = { + const tagOne: UserDAL.DBUserTag = { _id: new ObjectId(), name: "one", personalBests: {} as any, diff --git a/backend/__tests__/middlewares/auth.spec.ts b/backend/__tests__/middlewares/auth.spec.ts index 5c52f41f5..654f7bba5 100644 --- a/backend/__tests__/middlewares/auth.spec.ts +++ b/backend/__tests__/middlewares/auth.spec.ts @@ -14,6 +14,7 @@ import { RequestAuthenticationOptions, } from "@monkeytype/contracts/schemas/api"; import * as Prometheus from "../../src/utils/prometheus"; +import { TsRestRequestWithContext } from "../../src/api/types"; const mockDecodedToken: DecodedIdToken = { uid: "123456789", @@ -37,7 +38,7 @@ const mockApeKey = { vi.spyOn(ApeKeys, "getApeKey").mockResolvedValue(mockApeKey); vi.spyOn(ApeKeys, "updateLastUsedOn").mockResolvedValue(); const isDevModeMock = vi.spyOn(Misc, "isDevEnvironment"); -let mockRequest: Partial<Auth.TsRestRequestWithCtx>; +let mockRequest: Partial<TsRestRequestWithContext>; let mockResponse: Partial<Response>; let nextFunction: NextFunction; @@ -553,7 +554,7 @@ describe("middlewares/auth", () => { async function authenticate( request: Partial<Request>, authenticationOptions?: RequestAuthenticationOptions -): Promise<{ decodedToken: MonkeyTypes.DecodedToken }> { +): Promise<{ decodedToken: Auth.DecodedToken }> { const mergedRequest = { ...mockRequest, ...request, diff --git a/backend/__tests__/middlewares/configuration.spec.ts b/backend/__tests__/middlewares/configuration.spec.ts index 733235a51..8a63c601b 100644 --- a/backend/__tests__/middlewares/configuration.spec.ts +++ b/backend/__tests__/middlewares/configuration.spec.ts @@ -3,6 +3,7 @@ import { verifyRequiredConfiguration } from "../../src/middlewares/configuration import { Configuration } from "@monkeytype/contracts/schemas/configuration"; import { Response } from "express"; import MonkeyError from "../../src/utils/error"; +import { TsRestRequest } from "../../src/api/types"; describe("configuration middleware", () => { const handler = verifyRequiredConfiguration(); diff --git a/backend/__tests__/middlewares/permission.spec.ts b/backend/__tests__/middlewares/permission.spec.ts index 19146ba2d..b87c14f13 100644 --- a/backend/__tests__/middlewares/permission.spec.ts +++ b/backend/__tests__/middlewares/permission.spec.ts @@ -5,6 +5,8 @@ import * as Misc from "../../src/utils/misc"; import * as AdminUids from "../../src/dal/admin-uids"; import * as UserDal from "../../src/dal/user"; import MonkeyError from "../../src/utils/error"; +import { DecodedToken } from "../../src/middlewares/auth"; +import { TsRestRequest } from "../../src/api/types"; const uid = "123456789"; @@ -311,7 +313,7 @@ describe("permission middleware", () => { function givenRequest( metadata: EndpointMetadata, - decodedToken?: Partial<MonkeyTypes.DecodedToken> + decodedToken?: Partial<DecodedToken> ): TsRestRequest { return { tsRestRoute: { metadata }, ctx: { decodedToken } } as any; } diff --git a/backend/__tests__/tsconfig.json b/backend/__tests__/tsconfig.json index 001f654f4..44bc60c8f 100644 --- a/backend/__tests__/tsconfig.json +++ b/backend/__tests__/tsconfig.json @@ -7,6 +7,6 @@ "ts-node": { "files": true }, - "files": ["../src/types/types.d.ts", "vitest.d.ts"], + "files": ["vitest.d.ts"], "include": ["./**/*.ts", "./**/*.spec.ts", "./setup-tests.ts"] } diff --git a/backend/__tests__/utils/daily-leaderboards.spec.ts b/backend/__tests__/utils/daily-leaderboards.spec.ts index ad65e6b9c..041ef06af 100644 --- a/backend/__tests__/utils/daily-leaderboards.spec.ts +++ b/backend/__tests__/utils/daily-leaderboards.spec.ts @@ -1,3 +1,4 @@ +import { Mode } from "@monkeytype/contracts/schemas/shared"; import { getDailyLeaderboard } from "../../src/utils/daily-leaderboards"; const dailyLeaderboardsConfig = { @@ -77,7 +78,7 @@ describe("Daily Leaderboards", () => { modeCases.forEach(({ case: { language, mode, mode2 }, expected }) => { const result = getDailyLeaderboard( language, - mode, + mode as Mode, mode2, dailyLeaderboardsConfig ); diff --git a/backend/__tests__/utils/pb.spec.ts b/backend/__tests__/utils/pb.spec.ts index 79c80693d..5fe1366f8 100644 --- a/backend/__tests__/utils/pb.spec.ts +++ b/backend/__tests__/utils/pb.spec.ts @@ -59,7 +59,7 @@ describe("Pb Utils", () => { const run = pb.checkAndUpdatePb( userPbs, - {} as MonkeyTypes.LbPersonalBests, + {} as pb.LbPersonalBests, result ); @@ -168,7 +168,7 @@ describe("Pb Utils", () => { for (const lbPb of lbpbstartingvalues) { const lbPbPb = pb.updateLeaderboardPersonalBests( userPbs, - _.cloneDeep(lbPb) as MonkeyTypes.LbPersonalBests, + _.cloneDeep(lbPb) as pb.LbPersonalBests, result15 ); diff --git a/backend/src/api/controllers/admin.ts b/backend/src/api/controllers/admin.ts index 518c50e05..1ae73486a 100644 --- a/backend/src/api/controllers/admin.ts +++ b/backend/src/api/controllers/admin.ts @@ -14,13 +14,14 @@ import { import MonkeyError, { getErrorMessage } from "../../utils/error"; import { Configuration } from "@monkeytype/contracts/schemas/configuration"; import { addImportantLog } from "../../dal/logs"; +import { MonkeyRequest } from "../types"; -export async function test(_req: MonkeyTypes.Request): Promise<MonkeyResponse> { +export async function test(_req: MonkeyRequest): Promise<MonkeyResponse> { return new MonkeyResponse("OK", null); } export async function toggleBan( - req: MonkeyTypes.Request<undefined, ToggleBanRequest> + req: MonkeyRequest<undefined, ToggleBanRequest> ): Promise<ToggleBanResponse> { const { uid } = req.body; @@ -42,7 +43,7 @@ export async function toggleBan( } export async function acceptReports( - req: MonkeyTypes.Request<undefined, AcceptReportsRequest> + req: MonkeyRequest<undefined, AcceptReportsRequest> ): Promise<MonkeyResponse> { await handleReports( req.body.reports.map((it) => ({ ...it })), @@ -53,7 +54,7 @@ export async function acceptReports( } export async function rejectReports( - req: MonkeyTypes.Request<undefined, RejectReportsRequest> + req: MonkeyRequest<undefined, RejectReportsRequest> ): Promise<MonkeyResponse> { await handleReports( req.body.reports.map((it) => ({ ...it })), @@ -127,7 +128,7 @@ export async function handleReports( } export async function sendForgotPasswordEmail( - req: MonkeyTypes.Request<undefined, SendForgotPasswordEmailRequest> + req: MonkeyRequest<undefined, SendForgotPasswordEmailRequest> ): Promise<MonkeyResponse> { const { email } = req.body; await authSendForgotPasswordEmail(email); diff --git a/backend/src/api/controllers/ape-key.ts b/backend/src/api/controllers/ape-key.ts index 755088df0..5ff616f34 100644 --- a/backend/src/api/controllers/ape-key.ts +++ b/backend/src/api/controllers/ape-key.ts @@ -15,13 +15,14 @@ import { GetApeKeyResponse, } from "@monkeytype/contracts/ape-keys"; import { ApeKey } from "@monkeytype/contracts/schemas/ape-keys"; +import { MonkeyRequest } from "../types"; -function cleanApeKey(apeKey: MonkeyTypes.ApeKeyDB): ApeKey { +function cleanApeKey(apeKey: ApeKeysDAL.DBApeKey): ApeKey { return _.omit(apeKey, "hash", "_id", "uid", "useCount"); } export async function getApeKeys( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetApeKeyResponse> { const { uid } = req.ctx.decodedToken; @@ -32,7 +33,7 @@ export async function getApeKeys( } export async function generateApeKey( - req: MonkeyTypes.Request<undefined, AddApeKeyRequest> + req: MonkeyRequest<undefined, AddApeKeyRequest> ): Promise<AddApeKeyResponse> { const { name, enabled } = req.body; const { uid } = req.ctx.decodedToken; @@ -48,7 +49,7 @@ export async function generateApeKey( const apiKey = randomBytes(apeKeyBytes).toString("base64url"); const saltyHash = await hash(apiKey, apeKeySaltRounds); - const apeKey: MonkeyTypes.ApeKeyDB = { + const apeKey: ApeKeysDAL.DBApeKey = { _id: new ObjectId(), name, enabled, @@ -70,7 +71,7 @@ export async function generateApeKey( } export async function editApeKey( - req: MonkeyTypes.Request<undefined, EditApeKeyRequest, ApeKeyParams> + req: MonkeyRequest<undefined, EditApeKeyRequest, ApeKeyParams> ): Promise<MonkeyResponse> { const { apeKeyId } = req.params; const { name, enabled } = req.body; @@ -82,7 +83,7 @@ export async function editApeKey( } export async function deleteApeKey( - req: MonkeyTypes.Request<undefined, undefined, ApeKeyParams> + req: MonkeyRequest<undefined, undefined, ApeKeyParams> ): Promise<MonkeyResponse> { const { apeKeyId } = req.params; const { uid } = req.ctx.decodedToken; diff --git a/backend/src/api/controllers/config.ts b/backend/src/api/controllers/config.ts index 39569ebd0..afd68c875 100644 --- a/backend/src/api/controllers/config.ts +++ b/backend/src/api/controllers/config.ts @@ -2,9 +2,10 @@ import { PartialConfig } from "@monkeytype/contracts/schemas/configs"; import * as ConfigDAL from "../../dal/config"; import { MonkeyResponse } from "../../utils/monkey-response"; import { GetConfigResponse } from "@monkeytype/contracts/configs"; +import { MonkeyRequest } from "../types"; export async function getConfig( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetConfigResponse> { const { uid } = req.ctx.decodedToken; const data = (await ConfigDAL.getConfig(uid))?.config ?? null; @@ -13,7 +14,7 @@ export async function getConfig( } export async function saveConfig( - req: MonkeyTypes.Request<undefined, PartialConfig> + req: MonkeyRequest<undefined, PartialConfig> ): Promise<MonkeyResponse> { const config = req.body; const { uid } = req.ctx.decodedToken; @@ -24,7 +25,7 @@ export async function saveConfig( } export async function deleteConfig( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; diff --git a/backend/src/api/controllers/configuration.ts b/backend/src/api/controllers/configuration.ts index 35d172897..46978d597 100644 --- a/backend/src/api/controllers/configuration.ts +++ b/backend/src/api/controllers/configuration.ts @@ -7,16 +7,17 @@ import { PatchConfigurationRequest, } from "@monkeytype/contracts/configuration"; import MonkeyError from "../../utils/error"; +import { MonkeyRequest } from "../types"; export async function getConfiguration( - _req: MonkeyTypes.Request + _req: MonkeyRequest ): Promise<GetConfigurationResponse> { const currentConfiguration = await Configuration.getLiveConfiguration(); return new MonkeyResponse("Configuration retrieved", currentConfiguration); } export async function getSchema( - _req: MonkeyTypes.Request + _req: MonkeyRequest ): Promise<ConfigurationSchemaResponse> { return new MonkeyResponse( "Configuration schema retrieved", @@ -25,7 +26,7 @@ export async function getSchema( } export async function updateConfiguration( - req: MonkeyTypes.Request<undefined, PatchConfigurationRequest> + req: MonkeyRequest<undefined, PatchConfigurationRequest> ): Promise<MonkeyResponse> { const { configuration } = req.body; const success = await Configuration.patchConfiguration(configuration); diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts index 2b4eff576..26aa7d6b9 100644 --- a/backend/src/api/controllers/dev.ts +++ b/backend/src/api/controllers/dev.ts @@ -19,6 +19,9 @@ import { GenerateDataResponse, } from "@monkeytype/contracts/dev"; import { roundTo2 } from "@monkeytype/util/numbers"; +import { MonkeyRequest } from "../types"; +import { DBResult } from "../../utils/result"; +import { LbPersonalBests } from "../../utils/pb"; const CREATE_RESULT_DEFAULT_OPTIONS = { firstTestTimestamp: DateUtils.startOfDay(new UTCDate(Date.now())).valueOf(), @@ -28,7 +31,7 @@ const CREATE_RESULT_DEFAULT_OPTIONS = { }; export async function createTestData( - req: MonkeyTypes.Request<undefined, GenerateDataRequest> + req: MonkeyRequest<undefined, GenerateDataRequest> ): Promise<GenerateDataResponse> { const { username, createUser } = req.body; const user = await getOrCreateUser(username, "password", createUser); @@ -46,7 +49,7 @@ async function getOrCreateUser( username: string, password: string, createUser = false -): Promise<MonkeyTypes.DBUser> { +): Promise<UserDal.DBUser> { const existingUser = await UserDal.findByName(username); if (existingUser !== undefined && existingUser !== null) { @@ -69,7 +72,7 @@ async function getOrCreateUser( } async function createTestResults( - user: MonkeyTypes.DBUser, + user: UserDal.DBUser, configOptions: GenerateDataRequest ): Promise<void> { const config = { @@ -110,9 +113,9 @@ function random(min: number, max: number): number { } function createResult( - user: MonkeyTypes.DBUser, + user: UserDal.DBUser, timestamp: Date //evil, we modify this value -): MonkeyTypes.DBResult { +): DBResult { const mode: Mode = randomValue(["time", "words"]); const mode2: number = mode === "time" @@ -187,7 +190,7 @@ async function updateUser(uid: string): Promise<void> { ); //update PBs - const lbPersonalBests: MonkeyTypes.LbPersonalBests = { + const lbPersonalBests: LbPersonalBests = { time: { 15: {}, 60: {}, @@ -222,7 +225,7 @@ async function updateUser(uid: string): Promise<void> { .sort({ wpm: -1, timestamp: 1 }) .limit(1) .toArray() - )[0] as MonkeyTypes.DBResult; + )[0] as DBResult; if (personalBests[mode.mode] === undefined) personalBests[mode.mode] = {}; if (personalBests[mode.mode][mode.mode2] === undefined) diff --git a/backend/src/api/controllers/leaderboard.ts b/backend/src/api/controllers/leaderboard.ts index 720af4784..129004ed8 100644 --- a/backend/src/api/controllers/leaderboard.ts +++ b/backend/src/api/controllers/leaderboard.ts @@ -22,9 +22,10 @@ import { getCurrentWeekTimestamp, MILLISECONDS_IN_DAY, } from "@monkeytype/util/date-and-time"; +import { MonkeyRequest } from "../types"; export async function getLeaderboard( - req: MonkeyTypes.Request<GetLeaderboardQuery> + req: MonkeyRequest<GetLeaderboardQuery> ): Promise<GetLeaderboardResponse> { const { language, mode, mode2, skip = 0, limit = 50 } = req.query; @@ -49,7 +50,7 @@ export async function getLeaderboard( } export async function getRankFromLeaderboard( - req: MonkeyTypes.Request<LanguageAndModeQuery> + req: MonkeyRequest<LanguageAndModeQuery> ): Promise<GetLeaderboardRankResponse> { const { language, mode, mode2 } = req.query; const { uid } = req.ctx.decodedToken; @@ -89,7 +90,7 @@ function getDailyLeaderboardWithError( } export async function getDailyLeaderboard( - req: MonkeyTypes.Request<GetDailyLeaderboardQuery> + req: MonkeyRequest<GetDailyLeaderboardQuery> ): Promise<GetLeaderboardResponse> { const { skip = 0, limit = 50 } = req.query; @@ -112,7 +113,7 @@ export async function getDailyLeaderboard( } export async function getDailyLeaderboardRank( - req: MonkeyTypes.Request<GetDailyLeaderboardRankQuery> + req: MonkeyRequest<GetDailyLeaderboardRankQuery> ): Promise<GetLeaderboardDailyRankResponse> { const { uid } = req.ctx.decodedToken; @@ -147,7 +148,7 @@ function getWeeklyXpLeaderboardWithError( } export async function getWeeklyXpLeaderboardResults( - req: MonkeyTypes.Request<GetWeeklyXpLeaderboardQuery> + req: MonkeyRequest<GetWeeklyXpLeaderboardQuery> ): Promise<GetWeeklyXpLeaderboardResponse> { const { skip = 0, limit = 50 } = req.query; @@ -168,7 +169,7 @@ export async function getWeeklyXpLeaderboardResults( } export async function getWeeklyXpLeaderboardRank( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetWeeklyXpLeaderboardRankResponse> { const { uid } = req.ctx.decodedToken; diff --git a/backend/src/api/controllers/preset.ts b/backend/src/api/controllers/preset.ts index 2cf15715a..361acdd7c 100644 --- a/backend/src/api/controllers/preset.ts +++ b/backend/src/api/controllers/preset.ts @@ -8,9 +8,10 @@ import * as PresetDAL from "../../dal/preset"; import { MonkeyResponse } from "../../utils/monkey-response"; import { replaceObjectId } from "../../utils/misc"; import { EditPresetRequest } from "@monkeytype/contracts/schemas/presets"; +import { MonkeyRequest } from "../types"; export async function getPresets( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetPresetResponse> { const { uid } = req.ctx.decodedToken; @@ -25,7 +26,7 @@ export async function getPresets( } export async function addPreset( - req: MonkeyTypes.Request<undefined, AddPresetRequest> + req: MonkeyRequest<undefined, AddPresetRequest> ): Promise<AddPresetResponse> { const { uid } = req.ctx.decodedToken; @@ -35,7 +36,7 @@ export async function addPreset( } export async function editPreset( - req: MonkeyTypes.Request<undefined, EditPresetRequest> + req: MonkeyRequest<undefined, EditPresetRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; @@ -45,7 +46,7 @@ export async function editPreset( } export async function removePreset( - req: MonkeyTypes.Request<undefined, undefined, DeletePresetsParams> + req: MonkeyRequest<undefined, undefined, DeletePresetsParams> ): Promise<MonkeyResponse> { const { presetId } = req.params; const { uid } = req.ctx.decodedToken; diff --git a/backend/src/api/controllers/psa.ts b/backend/src/api/controllers/psa.ts index e70abb428..c5f561807 100644 --- a/backend/src/api/controllers/psa.ts +++ b/backend/src/api/controllers/psa.ts @@ -2,10 +2,9 @@ import { GetPsaResponse } from "@monkeytype/contracts/psas"; import * as PsaDAL from "../../dal/psa"; import { MonkeyResponse } from "../../utils/monkey-response"; import { replaceObjectIds } from "../../utils/misc"; +import { MonkeyRequest } from "../types"; -export async function getPsas( - _req: MonkeyTypes.Request -): Promise<GetPsaResponse> { +export async function getPsas(_req: MonkeyRequest): Promise<GetPsaResponse> { const data = await PsaDAL.get(); return new MonkeyResponse("PSAs retrieved", replaceObjectIds(data)); } diff --git a/backend/src/api/controllers/public.ts b/backend/src/api/controllers/public.ts index 64ae2d384..093cffb6d 100644 --- a/backend/src/api/controllers/public.ts +++ b/backend/src/api/controllers/public.ts @@ -5,9 +5,10 @@ import { } from "@monkeytype/contracts/public"; import * as PublicDAL from "../../dal/public"; import { MonkeyResponse } from "../../utils/monkey-response"; +import { MonkeyRequest } from "../types"; export async function getSpeedHistogram( - req: MonkeyTypes.Request<GetSpeedHistogramQuery> + req: MonkeyRequest<GetSpeedHistogramQuery> ): Promise<GetSpeedHistogramResponse> { const { language, mode, mode2 } = req.query; const data = await PublicDAL.getSpeedHistogram(language, mode, mode2); @@ -15,7 +16,7 @@ export async function getSpeedHistogram( } export async function getTypingStats( - _req: MonkeyTypes.Request + _req: MonkeyRequest ): Promise<GetTypingStatsResponse> { const data = await PublicDAL.getTypingStats(); return new MonkeyResponse("Public typing stats retrieved", data); diff --git a/backend/src/api/controllers/quote.ts b/backend/src/api/controllers/quote.ts index 103b8f6fe..ae8981a97 100644 --- a/backend/src/api/controllers/quote.ts +++ b/backend/src/api/controllers/quote.ts @@ -22,6 +22,7 @@ import { ReportQuoteRequest, } from "@monkeytype/contracts/quotes"; import { replaceObjectId, replaceObjectIds } from "../../utils/misc"; +import { MonkeyRequest } from "../types"; async function verifyCaptcha(captcha: string): Promise<void> { if (!(await verify(captcha))) { @@ -30,7 +31,7 @@ async function verifyCaptcha(captcha: string): Promise<void> { } export async function getQuotes( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetQuotesResponse> { const { uid } = req.ctx.decodedToken; const quoteMod = (await getPartialUser(uid, "get quotes", ["quoteMod"])) @@ -45,7 +46,7 @@ export async function getQuotes( } export async function isSubmissionEnabled( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<IsSubmissionEnabledResponse> { const { submissionsEnabled } = req.ctx.configuration.quotes; return new MonkeyResponse( @@ -55,7 +56,7 @@ export async function isSubmissionEnabled( } export async function addQuote( - req: MonkeyTypes.Request<undefined, AddQuoteRequest> + req: MonkeyRequest<undefined, AddQuoteRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { text, source, language, captcha } = req.body; @@ -67,7 +68,7 @@ export async function addQuote( } export async function approveQuote( - req: MonkeyTypes.Request<undefined, ApproveQuoteRequest> + req: MonkeyRequest<undefined, ApproveQuoteRequest> ): Promise<ApproveQuoteResponse> { const { uid } = req.ctx.decodedToken; const { quoteId, editText, editSource } = req.body; @@ -85,7 +86,7 @@ export async function approveQuote( } export async function refuseQuote( - req: MonkeyTypes.Request<undefined, RejectQuoteRequest> + req: MonkeyRequest<undefined, RejectQuoteRequest> ): Promise<MonkeyResponse> { const { quoteId } = req.body; @@ -94,7 +95,7 @@ export async function refuseQuote( } export async function getRating( - req: MonkeyTypes.Request<GetQuoteRatingQuery> + req: MonkeyRequest<GetQuoteRatingQuery> ): Promise<GetQuoteRatingResponse> { const { quoteId, language } = req.query; @@ -104,7 +105,7 @@ export async function getRating( } export async function submitRating( - req: MonkeyTypes.Request<undefined, AddQuoteRatingRequest> + req: MonkeyRequest<undefined, AddQuoteRatingRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { quoteId, rating, language } = req.body; @@ -135,7 +136,7 @@ export async function submitRating( } export async function reportQuote( - req: MonkeyTypes.Request<undefined, ReportQuoteRequest> + req: MonkeyRequest<undefined, ReportQuoteRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { @@ -146,7 +147,7 @@ export async function reportQuote( await verifyCaptcha(captcha); - const newReport: MonkeyTypes.Report = { + const newReport: ReportDAL.DBReport = { _id: new ObjectId(), id: uuidv4(), type: "quote", diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index 27ef99465..0dc5d5ec0 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -27,7 +27,11 @@ import _, { omit } from "lodash"; import * as WeeklyXpLeaderboard from "../../services/weekly-xp-leaderboard"; import { UAParser } from "ua-parser-js"; import { canFunboxGetPb } from "../../utils/pb"; -import { buildDbResult, replaceLegacyValues } from "../../utils/result"; +import { + buildDbResult, + DBResult, + replaceLegacyValues, +} from "../../utils/result"; import { Configuration } from "@monkeytype/contracts/schemas/configuration"; import { addLog } from "../../dal/logs"; import { @@ -52,6 +56,7 @@ import { getCurrentDayTimestamp, getStartOfDayTimestamp, } from "@monkeytype/util/date-and-time"; +import { MonkeyRequest } from "../types"; try { if (!anticheatImplemented()) throw new Error("undefined"); @@ -70,7 +75,7 @@ try { } export async function getResults( - req: MonkeyTypes.Request<GetResultsQuery> + req: MonkeyRequest<GetResultsQuery> ): Promise<GetResultsResponse> { const { uid } = req.ctx.decodedToken; const premiumFeaturesEnabled = req.ctx.configuration.users.premium.enabled; @@ -123,16 +128,14 @@ export async function getResults( } export async function getLastResult( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetLastResultResponse> { const { uid } = req.ctx.decodedToken; const results = await ResultDAL.getLastResult(uid); return new MonkeyResponse("Result retrieved", convertResult(results)); } -export async function deleteAll( - req: MonkeyTypes.Request -): Promise<MonkeyResponse> { +export async function deleteAll(req: MonkeyRequest): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; await ResultDAL.deleteAll(uid); @@ -141,7 +144,7 @@ export async function deleteAll( } export async function updateTags( - req: MonkeyTypes.Request<undefined, UpdateResultTagsRequest> + req: MonkeyRequest<undefined, UpdateResultTagsRequest> ): Promise<UpdateResultTagsResponse> { const { uid } = req.ctx.decodedToken; const { tagIds, resultId } = req.body; @@ -176,7 +179,7 @@ export async function updateTags( } export async function addResult( - req: MonkeyTypes.Request<undefined, AddResultRequest> + req: MonkeyRequest<undefined, AddResultRequest> ): Promise<AddResultResponse> { const { uid } = req.ctx.decodedToken; @@ -799,6 +802,6 @@ async function calculateXp( }; } -function convertResult(db: MonkeyTypes.DBResult): Result<Mode> { +function convertResult(db: DBResult): Result<Mode> { return replaceObjectId(replaceLegacyValues(db)); } diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index 2b7c78517..755ec52fe 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -86,6 +86,7 @@ import { UpdateUserProfileResponse, } from "@monkeytype/contracts/users"; import { MILLISECONDS_IN_DAY } from "@monkeytype/util/date-and-time"; +import { MonkeyRequest } from "../types"; async function verifyCaptcha(captcha: string): Promise<void> { let verified = false; @@ -104,7 +105,7 @@ async function verifyCaptcha(captcha: string): Promise<void> { } export async function createNewUser( - req: MonkeyTypes.Request<undefined, CreateUserRequest> + req: MonkeyRequest<undefined, CreateUserRequest> ): Promise<MonkeyResponse> { const { name, captcha } = req.body; const { email, uid } = req.ctx.decodedToken; @@ -138,7 +139,7 @@ export async function createNewUser( } export async function sendVerificationEmail( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<MonkeyResponse> { const { email, uid } = req.ctx.decodedToken; const isVerified = ( @@ -235,7 +236,7 @@ export async function sendVerificationEmail( } export async function sendForgotPasswordEmail( - req: MonkeyTypes.Request<undefined, ForgotPasswordEmailRequest> + req: MonkeyRequest<undefined, ForgotPasswordEmailRequest> ): Promise<MonkeyResponse> { const { email } = req.body; await authSendForgotPasswordEmail(email); @@ -245,9 +246,7 @@ export async function sendForgotPasswordEmail( ); } -export async function deleteUser( - req: MonkeyTypes.Request -): Promise<MonkeyResponse> { +export async function deleteUser(req: MonkeyRequest): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const userInfo = await UserDAL.getPartialUser(uid, "delete user", [ @@ -287,9 +286,7 @@ export async function deleteUser( return new MonkeyResponse("User deleted", null); } -export async function resetUser( - req: MonkeyTypes.Request -): Promise<MonkeyResponse> { +export async function resetUser(req: MonkeyRequest): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const userInfo = await UserDAL.getPartialUser(uid, "reset user", [ @@ -324,7 +321,7 @@ export async function resetUser( } export async function updateName( - req: MonkeyTypes.Request<undefined, UpdateUserNameRequest> + req: MonkeyRequest<undefined, UpdateUserNameRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { name } = req.body; @@ -357,9 +354,7 @@ export async function updateName( return new MonkeyResponse("User's name updated", null); } -export async function clearPb( - req: MonkeyTypes.Request -): Promise<MonkeyResponse> { +export async function clearPb(req: MonkeyRequest): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; await UserDAL.clearPb(uid); @@ -373,7 +368,7 @@ export async function clearPb( } export async function optOutOfLeaderboards( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; @@ -388,7 +383,7 @@ export async function optOutOfLeaderboards( } export async function checkName( - req: MonkeyTypes.Request<undefined, undefined, CheckNamePathParameters> + req: MonkeyRequest<undefined, undefined, CheckNamePathParameters> ): Promise<MonkeyResponse> { const { name } = req.params; const { uid } = req.ctx.decodedToken; @@ -402,7 +397,7 @@ export async function checkName( } export async function updateEmail( - req: MonkeyTypes.Request<undefined, UpdateEmailRequestSchema> + req: MonkeyRequest<undefined, UpdateEmailRequestSchema> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; let { newEmail } = req.body; @@ -448,7 +443,7 @@ export async function updateEmail( } export async function updatePassword( - req: MonkeyTypes.Request<undefined, UpdatePasswordRequest> + req: MonkeyRequest<undefined, UpdatePasswordRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { newPassword } = req.body; @@ -459,7 +454,7 @@ export async function updatePassword( } type RelevantUserInfo = Omit< - MonkeyTypes.DBUser, + UserDAL.DBUser, | "bananas" | "lbPersonalBests" | "inbox" @@ -472,7 +467,7 @@ type RelevantUserInfo = Omit< | "testActivity" >; -function getRelevantUserInfo(user: MonkeyTypes.DBUser): RelevantUserInfo { +function getRelevantUserInfo(user: UserDAL.DBUser): RelevantUserInfo { return _.omit(user, [ "bananas", "lbPersonalBests", @@ -487,12 +482,10 @@ function getRelevantUserInfo(user: MonkeyTypes.DBUser): RelevantUserInfo { ]) as RelevantUserInfo; } -export async function getUser( - req: MonkeyTypes.Request -): Promise<GetUserResponse> { +export async function getUser(req: MonkeyRequest): Promise<GetUserResponse> { const { uid } = req.ctx.decodedToken; - let userInfo: MonkeyTypes.DBUser; + let userInfo: UserDAL.DBUser; try { userInfo = await UserDAL.getUser(uid, "get user"); } catch (e) { @@ -535,7 +528,7 @@ export async function getUser( custom: {}, }; - const agentLog = buildAgentLog(req.raw); + const agentLog = buildAgentLog(req); void addLog("user_data_requested", agentLog, uid); void UserDAL.logIpAddress(uid, agentLog.ip, userInfo); @@ -585,7 +578,7 @@ export async function getUser( } export async function getOauthLink( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetDiscordOauthLinkResponse> { const { uid } = req.ctx.decodedToken; @@ -599,7 +592,7 @@ export async function getOauthLink( } export async function linkDiscord( - req: MonkeyTypes.Request<undefined, LinkDiscordRequest> + req: MonkeyRequest<undefined, LinkDiscordRequest> ): Promise<LinkDiscordResponse> { const { uid } = req.ctx.decodedToken; const { tokenType, accessToken, state } = req.body; @@ -659,7 +652,7 @@ export async function linkDiscord( } export async function unlinkDiscord( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; @@ -685,7 +678,7 @@ export async function unlinkDiscord( } export async function addResultFilterPreset( - req: MonkeyTypes.Request<undefined, AddResultFilterPresetRequest> + req: MonkeyRequest<undefined, AddResultFilterPresetRequest> ): Promise<AddResultFilterPresetResponse> { const { uid } = req.ctx.decodedToken; const filter = req.body; @@ -703,11 +696,7 @@ export async function addResultFilterPreset( } export async function removeResultFilterPreset( - req: MonkeyTypes.Request< - undefined, - undefined, - RemoveResultFilterPresetPathParams - > + req: MonkeyRequest<undefined, undefined, RemoveResultFilterPresetPathParams> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { presetId } = req.params; @@ -717,7 +706,7 @@ export async function removeResultFilterPreset( } export async function addTag( - req: MonkeyTypes.Request<undefined, AddTagRequest> + req: MonkeyRequest<undefined, AddTagRequest> ): Promise<AddTagResponse> { const { uid } = req.ctx.decodedToken; const { tagName } = req.body; @@ -727,7 +716,7 @@ export async function addTag( } export async function clearTagPb( - req: MonkeyTypes.Request<undefined, undefined, TagIdPathParams> + req: MonkeyRequest<undefined, undefined, TagIdPathParams> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { tagId } = req.params; @@ -737,7 +726,7 @@ export async function clearTagPb( } export async function editTag( - req: MonkeyTypes.Request<undefined, EditTagRequest> + req: MonkeyRequest<undefined, EditTagRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { tagId, newName } = req.body; @@ -747,7 +736,7 @@ export async function editTag( } export async function removeTag( - req: MonkeyTypes.Request<undefined, undefined, TagIdPathParams> + req: MonkeyRequest<undefined, undefined, TagIdPathParams> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { tagId } = req.params; @@ -756,9 +745,7 @@ export async function removeTag( return new MonkeyResponse("Tag deleted", null); } -export async function getTags( - req: MonkeyTypes.Request -): Promise<GetTagsResponse> { +export async function getTags(req: MonkeyRequest): Promise<GetTagsResponse> { const { uid } = req.ctx.decodedToken; const tags = await UserDAL.getTags(uid); @@ -766,7 +753,7 @@ export async function getTags( } export async function updateLbMemory( - req: MonkeyTypes.Request<undefined, UpdateLeaderboardMemoryRequest> + req: MonkeyRequest<undefined, UpdateLeaderboardMemoryRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { mode, language, rank } = req.body; @@ -777,7 +764,7 @@ export async function updateLbMemory( } export async function getCustomThemes( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetCustomThemesResponse> { const { uid } = req.ctx.decodedToken; const customThemes = await UserDAL.getThemes(uid); @@ -788,7 +775,7 @@ export async function getCustomThemes( } export async function addCustomTheme( - req: MonkeyTypes.Request<undefined, AddCustomThemeRequest> + req: MonkeyRequest<undefined, AddCustomThemeRequest> ): Promise<AddCustomThemeResponse> { const { uid } = req.ctx.decodedToken; const { name, colors } = req.body; @@ -798,7 +785,7 @@ export async function addCustomTheme( } export async function removeCustomTheme( - req: MonkeyTypes.Request<undefined, DeleteCustomThemeRequest> + req: MonkeyRequest<undefined, DeleteCustomThemeRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { themeId } = req.body; @@ -807,7 +794,7 @@ export async function removeCustomTheme( } export async function editCustomTheme( - req: MonkeyTypes.Request<undefined, EditCustomThemeRequst> + req: MonkeyRequest<undefined, EditCustomThemeRequst> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { themeId, theme } = req.body; @@ -817,7 +804,7 @@ export async function editCustomTheme( } export async function getPersonalBests( - req: MonkeyTypes.Request<GetPersonalBestsQuery> + req: MonkeyRequest<GetPersonalBestsQuery> ): Promise<GetPersonalBestsResponse> { const { uid } = req.ctx.decodedToken; const { mode, mode2 } = req.query; @@ -826,9 +813,7 @@ export async function getPersonalBests( return new MonkeyResponse("Personal bests retrieved", data); } -export async function getStats( - req: MonkeyTypes.Request -): Promise<GetStatsResponse> { +export async function getStats(req: MonkeyRequest): Promise<GetStatsResponse> { const { uid } = req.ctx.decodedToken; const data = (await UserDAL.getStats(uid)) ?? null; @@ -836,7 +821,7 @@ export async function getStats( } export async function getFavoriteQuotes( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetFavoriteQuotesResponse> { const { uid } = req.ctx.decodedToken; @@ -846,7 +831,7 @@ export async function getFavoriteQuotes( } export async function addFavoriteQuote( - req: MonkeyTypes.Request<undefined, AddFavoriteQuoteRequest> + req: MonkeyRequest<undefined, AddFavoriteQuoteRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; @@ -863,7 +848,7 @@ export async function addFavoriteQuote( } export async function removeFavoriteQuote( - req: MonkeyTypes.Request<undefined, RemoveFavoriteQuoteRequest> + req: MonkeyRequest<undefined, RemoveFavoriteQuoteRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; @@ -874,7 +859,7 @@ export async function removeFavoriteQuote( } export async function getProfile( - req: MonkeyTypes.Request<GetProfileQuery, undefined, GetProfilePathParams> + req: MonkeyRequest<GetProfileQuery, undefined, GetProfilePathParams> ): Promise<GetProfileResponse> { const { uidOrName } = req.params; @@ -946,7 +931,7 @@ export async function getProfile( } export async function updateProfile( - req: MonkeyTypes.Request<undefined, UpdateUserProfileRequest> + req: MonkeyRequest<undefined, UpdateUserProfileRequest> ): Promise<UpdateUserProfileResponse> { const { uid } = req.ctx.decodedToken; const { bio, keyboard, socialProfiles, selectedBadgeId } = req.body; @@ -983,7 +968,7 @@ export async function updateProfile( } export async function getInbox( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetUserInboxResponse> { const { uid } = req.ctx.decodedToken; @@ -996,7 +981,7 @@ export async function getInbox( } export async function updateInbox( - req: MonkeyTypes.Request<undefined, UpdateUserInboxRequest> + req: MonkeyRequest<undefined, UpdateUserInboxRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { mailIdsToMarkRead, mailIdsToDelete } = req.body; @@ -1011,7 +996,7 @@ export async function updateInbox( } export async function reportUser( - req: MonkeyTypes.Request<undefined, ReportUserRequest> + req: MonkeyRequest<undefined, ReportUserRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { @@ -1022,7 +1007,7 @@ export async function reportUser( await verifyCaptcha(captcha); - const newReport: MonkeyTypes.Report = { + const newReport: ReportDAL.DBReport = { _id: new ObjectId(), id: uuidv4(), type: "user", @@ -1039,7 +1024,7 @@ export async function reportUser( } export async function setStreakHourOffset( - req: MonkeyTypes.Request<undefined, SetStreakHourOffsetRequest> + req: MonkeyRequest<undefined, SetStreakHourOffsetRequest> ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; const { hourOffset } = req.body; @@ -1063,7 +1048,7 @@ export async function setStreakHourOffset( } export async function revokeAllTokens( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<MonkeyResponse> { const { uid } = req.ctx.decodedToken; await AuthUtil.revokeTokensByUid(uid); @@ -1151,7 +1136,7 @@ export function generateCurrentTestActivity( } export async function getTestActivity( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetTestActivityResponse> { const { uid } = req.ctx.decodedToken; const premiumFeaturesEnabled = req.ctx.configuration.users.premium.enabled; @@ -1184,7 +1169,7 @@ async function firebaseDeleteUserIgnoreError(uid: string): Promise<void> { } export async function getCurrentTestActivity( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetCurrentTestActivityResponse> { const { uid } = req.ctx.decodedToken; @@ -1199,7 +1184,7 @@ export async function getCurrentTestActivity( } export async function getStreak( - req: MonkeyTypes.Request + req: MonkeyRequest ): Promise<GetStreakResponseSchema> { const { uid } = req.ctx.decodedToken; diff --git a/backend/src/api/controllers/webhooks.ts b/backend/src/api/controllers/webhooks.ts index f6f7525b4..702ff2621 100644 --- a/backend/src/api/controllers/webhooks.ts +++ b/backend/src/api/controllers/webhooks.ts @@ -2,9 +2,10 @@ import { PostGithubReleaseRequest } from "@monkeytype/contracts/webhooks"; import GeorgeQueue from "../../queues/george-queue"; import { MonkeyResponse } from "../../utils/monkey-response"; import MonkeyError from "../../utils/error"; +import { MonkeyRequest } from "../types"; export async function githubRelease( - req: MonkeyTypes.Request<undefined, PostGithubReleaseRequest> + req: MonkeyRequest<undefined, PostGithubReleaseRequest> ): Promise<MonkeyResponse> { const action = req.body.action; diff --git a/backend/src/api/routes/index.ts b/backend/src/api/routes/index.ts index a11d9b092..91d061fd8 100644 --- a/backend/src/api/routes/index.ts +++ b/backend/src/api/routes/index.ts @@ -36,6 +36,7 @@ import { authenticateTsRestRequest } from "../../middlewares/auth"; import { rateLimitRequest } from "../../middlewares/rate-limit"; import { verifyPermissions } from "../../middlewares/permission"; import { verifyRequiredConfiguration } from "../../middlewares/configuration"; +import { ExpressRequestWithContext } from "../types"; const pathOverride = process.env["API_PATH_OVERRIDE"]; const BASE_ROUTE = pathOverride !== undefined ? `/${pathOverride}` : ""; @@ -156,7 +157,7 @@ function applyApiRoutes(app: Application): void { app.use( ( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, res: Response, next: NextFunction ): void => { diff --git a/backend/src/api/ts-rest-adapter.ts b/backend/src/api/ts-rest-adapter.ts index a48313473..587fad99c 100644 --- a/backend/src/api/ts-rest-adapter.ts +++ b/backend/src/api/ts-rest-adapter.ts @@ -1,6 +1,9 @@ import { AppRoute, AppRouter } from "@ts-rest/core"; import { TsRestRequest } from "@ts-rest/express"; import { MonkeyResponse } from "../utils/monkey-response"; +import { Context } from "../middlewares/context"; +import { MonkeyRequest } from "./types"; + export function callController< TRoute extends AppRoute | AppRouter, TQuery, @@ -11,18 +14,18 @@ export function callController< // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters TStatus = 200 >( - handler: Handler<TQuery, TBody, TParams, TResponse> -): (all: RequestType2<TRoute, TQuery, TBody, TParams>) => Promise<{ + handler: MonkeyHandler<TQuery, TBody, TParams, TResponse> +): (all: TypeSafeTsRestRequest<TRoute, TQuery, TBody, TParams>) => Promise<{ status: TStatus; - body: { message: string; data: TResponse }; + body: MonkeyResponse<TResponse>; }> { return async (all) => { - const req: MonkeyTypes.Request<TQuery, TBody, TParams> = { + const req: MonkeyRequest<TQuery, TBody, TParams> = { body: all.body as TBody, query: all.query as TQuery, params: all.params as TParams, raw: all.req, - ctx: all.req["ctx"] as MonkeyTypes.Context, + ctx: all.req["ctx"] as Context, }; const result = await handler(req); @@ -59,11 +62,11 @@ type WithoutParams = { params?: never; }; -type Handler<TQuery, TBody, TParams, TResponse> = ( - req: MonkeyTypes.Request<TQuery, TBody, TParams> +type MonkeyHandler<TQuery, TBody, TParams, TResponse> = ( + req: MonkeyRequest<TQuery, TBody, TParams> ) => Promise<MonkeyResponse<TResponse>>; -type RequestType2< +type TypeSafeTsRestRequest< TRoute extends AppRoute | AppRouter, TQuery, TBody, diff --git a/backend/src/api/types.ts b/backend/src/api/types.ts new file mode 100644 index 000000000..ec84f5950 --- /dev/null +++ b/backend/src/api/types.ts @@ -0,0 +1,27 @@ +import { TsRestRequest as TsRestRequestGeneric } from "@ts-rest/express"; +import { Request as ExpressRequest } from "express"; +import { Context } from "../middlewares/context"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type TsRestRequest = TsRestRequestGeneric<any>; + +export type ExpressRequestWithContext = { + ctx: Readonly<Context>; +} & ExpressRequest; + +export type TsRestRequestWithContext = { + ctx: Readonly<Context>; +} & TsRestRequest & + ExpressRequest; + +export type MonkeyRequest< + TQuery = undefined, + TBody = undefined, + TParams = undefined +> = { + query: Readonly<TQuery>; + body: Readonly<TBody>; + params: Readonly<TParams>; + ctx: Readonly<Context>; + raw: Readonly<TsRestRequest>; +}; diff --git a/backend/src/constants/funbox-list.ts b/backend/src/constants/funbox-list.ts index 07b003b5a..61b88a139 100644 --- a/backend/src/constants/funbox-list.ts +++ b/backend/src/constants/funbox-list.ts @@ -1,4 +1,13 @@ -const FunboxList: MonkeyTypes.FunboxMetadata[] = [ +export type FunboxMetadata = { + name: string; + canGetPb: boolean; + difficultyLevel: number; + properties?: string[]; + frontendForcedConfig?: Record<string, string[] | boolean[]>; + frontendFunctions?: string[]; +}; + +const FunboxList: FunboxMetadata[] = [ { canGetPb: false, difficultyLevel: 1, diff --git a/backend/src/dal/ape-keys.ts b/backend/src/dal/ape-keys.ts index 16a03a60a..2cf9ce8a3 100644 --- a/backend/src/dal/ape-keys.ts +++ b/backend/src/dal/ape-keys.ts @@ -8,28 +8,30 @@ import { Collection, } from "mongodb"; import MonkeyError from "../utils/error"; +import { ApeKey } from "@monkeytype/contracts/schemas/ape-keys"; -export const getApeKeysCollection = (): Collection< - WithId<MonkeyTypes.ApeKeyDB> -> => db.collection<MonkeyTypes.ApeKeyDB>("ape-keys"); +export type DBApeKey = ApeKey & { + _id: ObjectId; + uid: string; + hash: string; + useCount: number; +}; -function getApeKeyFilter( - uid: string, - keyId: string -): Filter<MonkeyTypes.ApeKeyDB> { +export const getApeKeysCollection = (): Collection<WithId<DBApeKey>> => + db.collection<DBApeKey>("ape-keys"); + +function getApeKeyFilter(uid: string, keyId: string): Filter<DBApeKey> { return { _id: new ObjectId(keyId), uid, }; } -export async function getApeKeys(uid: string): Promise<MonkeyTypes.ApeKeyDB[]> { +export async function getApeKeys(uid: string): Promise<DBApeKey[]> { return await getApeKeysCollection().find({ uid }).toArray(); } -export async function getApeKey( - keyId: string -): Promise<MonkeyTypes.ApeKeyDB | null> { +export async function getApeKey(keyId: string): Promise<DBApeKey | null> { return await getApeKeysCollection().findOne({ _id: new ObjectId(keyId) }); } @@ -37,7 +39,7 @@ export async function countApeKeysForUser(uid: string): Promise<number> { return getApeKeysCollection().countDocuments({ uid }); } -export async function addApeKey(apeKey: MonkeyTypes.ApeKeyDB): Promise<string> { +export async function addApeKey(apeKey: DBApeKey): Promise<string> { const insertionResult = await getApeKeysCollection().insertOne(apeKey); return insertionResult.insertedId.toHexString(); } @@ -45,7 +47,7 @@ export async function addApeKey(apeKey: MonkeyTypes.ApeKeyDB): Promise<string> { async function updateApeKey( uid: string, keyId: string, - updates: MatchKeysAndValues<MonkeyTypes.ApeKeyDB> + updates: MatchKeysAndValues<DBApeKey> ): Promise<void> { const updateResult = await getApeKeysCollection().updateOne( getApeKeyFilter(uid, keyId), diff --git a/backend/src/dal/blocklist.ts b/backend/src/dal/blocklist.ts index c6992fe27..5c0cf9c0d 100644 --- a/backend/src/dal/blocklist.ts +++ b/backend/src/dal/blocklist.ts @@ -2,10 +2,22 @@ import { Collection } from "mongodb"; import * as db from "../init/db"; import { createHash } from "crypto"; import { User } from "@monkeytype/contracts/schemas/users"; +import { WithObjectId } from "../utils/misc"; type BlocklistEntryProperties = Pick<User, "name" | "email" | "discordId">; + +type BlocklistEntry = { + _id: string; + usernameHash?: string; + emailHash?: string; + discordIdHash?: string; + timestamp: number; +}; + +type DBBlocklistEntry = WithObjectId<BlocklistEntry>; + // Export for use in tests -export const getCollection = (): Collection<MonkeyTypes.DBBlocklistEntry> => +export const getCollection = (): Collection<DBBlocklistEntry> => db.collection("blocklist"); export async function add(user: BlocklistEntryProperties): Promise<void> { @@ -75,8 +87,8 @@ export function hash(value: string): string { function getFilter( user: Partial<BlocklistEntryProperties> -): Partial<MonkeyTypes.DBBlocklistEntry>[] { - const filter: Partial<MonkeyTypes.DBBlocklistEntry>[] = []; +): Partial<DBBlocklistEntry>[] { + const filter: Partial<DBBlocklistEntry>[] = []; if (user.email !== undefined) { filter.push({ emailHash: hash(user.email) }); } diff --git a/backend/src/dal/config.ts b/backend/src/dal/config.ts index 67cd2abc4..a11b32fe5 100644 --- a/backend/src/dal/config.ts +++ b/backend/src/dal/config.ts @@ -1,4 +1,4 @@ -import { Collection, UpdateResult } from "mongodb"; +import { Collection, ObjectId, UpdateResult } from "mongodb"; import * as db from "../init/db"; import _ from "lodash"; import { Config, PartialConfig } from "@monkeytype/contracts/schemas/configs"; diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts index 9f833ca38..6d1f98118 100644 --- a/backend/src/dal/leaderboards.ts +++ b/backend/src/dal/leaderboards.ts @@ -6,12 +6,13 @@ import { isDevEnvironment } from "../utils/misc"; import { getCachedConfiguration } from "../init/configuration"; import { addLog } from "./logs"; -import { Collection } from "mongodb"; +import { Collection, ObjectId } from "mongodb"; import { LeaderboardEntry, LeaderboardRank, } from "@monkeytype/contracts/schemas/leaderboards"; import { omit } from "lodash"; +import { DBUser } from "./user"; export type DBLeaderboardEntry = LeaderboardEntry & { _id: ObjectId; @@ -104,77 +105,75 @@ export async function update( }> { const key = `lbPersonalBests.${mode}.${mode2}.${language}`; const lbCollectionName = `leaderboards.${language}.${mode}.${mode2}`; - const lb = db - .collection<MonkeyTypes.DBUser>("users") - .aggregate<LeaderboardEntry>( - [ - { - $match: { - [`${key}.wpm`]: { - $gt: 0, - }, - [`${key}.acc`]: { - $gt: 0, - }, - [`${key}.timestamp`]: { - $gt: 0, - }, - banned: { - $ne: true, - }, - lbOptOut: { - $ne: true, - }, - needsToChangeName: { - $ne: true, - }, - timeTyping: { - $gt: isDevEnvironment() ? 0 : 7200, - }, + const lb = db.collection<DBUser>("users").aggregate<LeaderboardEntry>( + [ + { + $match: { + [`${key}.wpm`]: { + $gt: 0, }, - }, - { - $sort: { - [`${key}.wpm`]: -1, - [`${key}.acc`]: -1, - [`${key}.timestamp`]: -1, + [`${key}.acc`]: { + $gt: 0, }, - }, - { - $project: { - _id: 0, - [`${key}.wpm`]: 1, - [`${key}.acc`]: 1, - [`${key}.raw`]: 1, - [`${key}.consistency`]: 1, - [`${key}.timestamp`]: 1, - uid: 1, - name: 1, - discordId: 1, - discordAvatar: 1, - inventory: 1, - premium: 1, + [`${key}.timestamp`]: { + $gt: 0, + }, + banned: { + $ne: true, }, + lbOptOut: { + $ne: true, + }, + needsToChangeName: { + $ne: true, + }, + timeTyping: { + $gt: isDevEnvironment() ? 0 : 7200, + }, + }, + }, + { + $sort: { + [`${key}.wpm`]: -1, + [`${key}.acc`]: -1, + [`${key}.timestamp`]: -1, + }, + }, + { + $project: { + _id: 0, + [`${key}.wpm`]: 1, + [`${key}.acc`]: 1, + [`${key}.raw`]: 1, + [`${key}.consistency`]: 1, + [`${key}.timestamp`]: 1, + uid: 1, + name: 1, + discordId: 1, + discordAvatar: 1, + inventory: 1, + premium: 1, }, + }, - { - $addFields: { - "user.uid": "$uid", - "user.name": "$name", - "user.discordId": { $ifNull: ["$discordId", "$$REMOVE"] }, - "user.discordAvatar": { $ifNull: ["$discordAvatar", "$$REMOVE"] }, - [`${key}.consistency`]: { - $ifNull: [`$${key}.consistency`, "$$REMOVE"], - }, - calculated: { - $function: { - lang: "js", - args: [ - "$premium.expirationTimestamp", - "$$NOW", - "$inventory.badges", - ], - body: `function(expiration, currentTime, badges) { + { + $addFields: { + "user.uid": "$uid", + "user.name": "$name", + "user.discordId": { $ifNull: ["$discordId", "$$REMOVE"] }, + "user.discordAvatar": { $ifNull: ["$discordAvatar", "$$REMOVE"] }, + [`${key}.consistency`]: { + $ifNull: [`$${key}.consistency`, "$$REMOVE"], + }, + calculated: { + $function: { + lang: "js", + args: [ + "$premium.expirationTimestamp", + "$$NOW", + "$inventory.badges", + ], + body: `function(expiration, currentTime, badges) { try {row_number+= 1;} catch (e) {row_number= 1;} var badgeId = undefined; if(badges)for(let i=0; i<badges.length; i++){ @@ -183,19 +182,19 @@ export async function update( var isPremium = expiration !== undefined && (expiration === -1 || new Date(expiration)>currentTime) || undefined; return {rank:row_number,badgeId, isPremium}; }`, - }, }, }, }, - { - $replaceWith: { - $mergeObjects: [`$${key}`, "$user", "$calculated"], - }, + }, + { + $replaceWith: { + $mergeObjects: [`$${key}`, "$user", "$calculated"], }, - { $out: lbCollectionName }, - ], - { allowDiskUse: true } - ); + }, + { $out: lbCollectionName }, + ], + { allowDiskUse: true } + ); const start1 = performance.now(); await lb.toArray(); diff --git a/backend/src/dal/new-quotes.ts b/backend/src/dal/new-quotes.ts index 54e5f1117..9e4752ba2 100644 --- a/backend/src/dal/new-quotes.ts +++ b/backend/src/dal/new-quotes.ts @@ -7,6 +7,7 @@ import * as db from "../init/db"; import MonkeyError from "../utils/error"; import { compareTwoStrings } from "string-similarity"; import { ApproveQuote, Quote } from "@monkeytype/contracts/schemas/quotes"; +import { WithObjectId } from "../utils/misc"; type JsonQuote = { text: string; @@ -38,7 +39,7 @@ type AddQuoteReturn = { similarityScore?: number; }; -export type DBNewQuote = MonkeyTypes.WithObjectId<Quote>; +export type DBNewQuote = WithObjectId<Quote>; // Export for use in tests export const getNewQuoteCollection = (): Collection<DBNewQuote> => diff --git a/backend/src/dal/preset.ts b/backend/src/dal/preset.ts index 0a7d5a0ca..0ffb3075e 100644 --- a/backend/src/dal/preset.ts +++ b/backend/src/dal/preset.ts @@ -6,10 +6,11 @@ import { Preset, } from "@monkeytype/contracts/schemas/presets"; import { omit } from "lodash"; +import { WithObjectId } from "../utils/misc"; const MAX_PRESETS = 10; -type DBConfigPreset = MonkeyTypes.WithObjectId< +type DBConfigPreset = WithObjectId< Preset & { uid: string; } diff --git a/backend/src/dal/psa.ts b/backend/src/dal/psa.ts index 904c30b8a..2f57bb2b0 100644 --- a/backend/src/dal/psa.ts +++ b/backend/src/dal/psa.ts @@ -1,7 +1,8 @@ import { PSA } from "@monkeytype/contracts/schemas/psas"; import * as db from "../init/db"; +import { WithObjectId } from "../utils/misc"; -export type DBPSA = MonkeyTypes.WithObjectId<PSA>; +export type DBPSA = WithObjectId<PSA>; export async function get(): Promise<DBPSA[]> { return await db.collection<DBPSA>("psa").find().toArray(); diff --git a/backend/src/dal/quote-ratings.ts b/backend/src/dal/quote-ratings.ts index 031582420..6046b5183 100644 --- a/backend/src/dal/quote-ratings.ts +++ b/backend/src/dal/quote-ratings.ts @@ -1,8 +1,9 @@ import { QuoteRating } from "@monkeytype/contracts/schemas/quotes"; import * as db from "../init/db"; import { Collection } from "mongodb"; +import { WithObjectId } from "../utils/misc"; -type DBQuoteRating = MonkeyTypes.WithObjectId<QuoteRating>; +type DBQuoteRating = WithObjectId<QuoteRating>; // Export for use in tests export const getQuoteRatingCollection = (): Collection<DBQuoteRating> => diff --git a/backend/src/dal/report.ts b/backend/src/dal/report.ts index 7f746019c..9f3b98332 100644 --- a/backend/src/dal/report.ts +++ b/backend/src/dal/report.ts @@ -1,16 +1,25 @@ import MonkeyError from "../utils/error"; import * as db from "../init/db"; +import { ObjectId } from "mongodb"; + +type ReportTypes = "quote" | "user"; + +export type DBReport = { + _id: ObjectId; + id: string; + type: ReportTypes; + timestamp: number; + uid: string; + contentId: string; + reason: string; + comment: string; +}; const COLLECTION_NAME = "reports"; -export async function getReports( - reportIds: string[] -): Promise<MonkeyTypes.Report[]> { +export async function getReports(reportIds: string[]): Promise<DBReport[]> { const query = { id: { $in: reportIds } }; - return await db - .collection<MonkeyTypes.Report>(COLLECTION_NAME) - .find(query) - .toArray(); + return await db.collection<DBReport>(COLLECTION_NAME).find(query).toArray(); } export async function deleteReports(reportIds: string[]): Promise<void> { @@ -19,7 +28,7 @@ export async function deleteReports(reportIds: string[]): Promise<void> { } export async function createReport( - report: MonkeyTypes.Report, + report: DBReport, maxReports: number, contentReportLimit: number ): Promise<void> { @@ -28,7 +37,7 @@ export async function createReport( } const reportsCount = await db - .collection<MonkeyTypes.Report>(COLLECTION_NAME) + .collection<DBReport>(COLLECTION_NAME) .estimatedDocumentCount(); if (reportsCount >= maxReports) { @@ -39,7 +48,7 @@ export async function createReport( } const sameReports = await db - .collection<MonkeyTypes.Report>(COLLECTION_NAME) + .collection<DBReport>(COLLECTION_NAME) .find({ contentId: report.contentId }) .toArray(); @@ -58,5 +67,5 @@ export async function createReport( throw new MonkeyError(409, "You have already reported this content."); } - await db.collection<MonkeyTypes.Report>(COLLECTION_NAME).insertOne(report); + await db.collection<DBReport>(COLLECTION_NAME).insertOne(report); } diff --git a/backend/src/dal/result.ts b/backend/src/dal/result.ts index dd1d80364..797829e38 100644 --- a/backend/src/dal/result.ts +++ b/backend/src/dal/result.ts @@ -8,16 +8,17 @@ import { import MonkeyError from "../utils/error"; import * as db from "../init/db"; -import { getUser, getTags } from "./user"; +import { getUser, getTags, DBUser } from "./user"; +import { DBResult } from "../utils/result"; -export const getResultCollection = (): Collection<MonkeyTypes.DBResult> => - db.collection<MonkeyTypes.DBResult>("results"); +export const getResultCollection = (): Collection<DBResult> => + db.collection<DBResult>("results"); export async function addResult( uid: string, - result: MonkeyTypes.DBResult + result: DBResult ): Promise<{ insertedId: ObjectId }> { - let user: MonkeyTypes.DBUser | null = null; + let user: DBUser | null = null; try { user = await getUser(uid, "add result"); } catch (e) { @@ -61,10 +62,7 @@ export async function updateTags( ); } -export async function getResult( - uid: string, - id: string -): Promise<MonkeyTypes.DBResult> { +export async function getResult(uid: string, id: string): Promise<DBResult> { const result = await getResultCollection().findOne({ _id: new ObjectId(id), uid, @@ -73,9 +71,7 @@ export async function getResult( return result; } -export async function getLastResult( - uid: string -): Promise<MonkeyTypes.DBResult> { +export async function getLastResult(uid: string): Promise<DBResult> { const [lastResult] = await getResultCollection() .find({ uid }) .sort({ timestamp: -1 }) @@ -88,7 +84,7 @@ export async function getLastResult( export async function getResultByTimestamp( uid: string, timestamp: number -): Promise<MonkeyTypes.DBResult | null> { +): Promise<DBResult | null> { return await getResultCollection().findOne({ uid, timestamp }); } @@ -101,7 +97,7 @@ type GetResultsOpts = { export async function getResults( uid: string, opts?: GetResultsOpts -): Promise<MonkeyTypes.DBResult[]> { +): Promise<DBResult[]> { const { onOrAfterTimestamp, offset, limit } = opts ?? {}; let query = getResultCollection() .find({ diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts index da652c2cc..07f016355 100644 --- a/backend/src/dal/user.ts +++ b/backend/src/dal/user.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import { canFunboxGetPb, checkAndUpdatePb } from "../utils/pb"; +import { canFunboxGetPb, checkAndUpdatePb, LbPersonalBests } from "../utils/pb"; import * as db from "../init/db"; import MonkeyError from "../utils/error"; import { @@ -9,7 +9,7 @@ import { type UpdateFilter, type Filter, } from "mongodb"; -import { flattenObjectDeep } from "../utils/misc"; +import { flattenObjectDeep, WithObjectId } from "../utils/misc"; import { getCachedConfiguration } from "../init/configuration"; import { getDayOfYear } from "date-fns"; import { UTCDate } from "@date-fns/utc"; @@ -23,6 +23,9 @@ import { UserQuoteRatings, UserStreak, ResultFilters, + UserTag, + User, + CountByYearAndDay, } from "@monkeytype/contracts/schemas/users"; import { Mode, @@ -34,20 +37,46 @@ import { Result as ResultType } from "@monkeytype/contracts/schemas/results"; import { Configuration } from "@monkeytype/contracts/schemas/configuration"; import { isToday, isYesterday } from "@monkeytype/util/date-and-time"; +export type DBUserTag = WithObjectId<UserTag>; + +export type DBUser = Omit< + User, + | "resultFilterPresets" + | "tags" + | "customThemes" + | "isPremium" + | "allTimeLbs" + | "testActivity" +> & { + _id: ObjectId; + resultFilterPresets?: WithObjectId<ResultFilters>[]; + tags?: DBUserTag[]; + lbPersonalBests?: LbPersonalBests; + customThemes?: WithObjectId<CustomTheme>[]; + autoBanTimestamps?: number[]; + inbox?: MonkeyMail[]; + ips?: string[]; + canReport?: boolean; + lastNameChange?: number; + canManageApeKeys?: boolean; + bananas?: number; + testActivity?: CountByYearAndDay; +}; + const SECONDS_PER_HOUR = 3600; type Result = Omit<ResultType<Mode>, "_id" | "name">; // Export for use in tests -export const getUsersCollection = (): Collection<MonkeyTypes.DBUser> => - db.collection<MonkeyTypes.DBUser>("users"); +export const getUsersCollection = (): Collection<DBUser> => + db.collection<DBUser>("users"); export async function addUser( name: string, email: string, uid: string ): Promise<void> { - const newUserDocument: Partial<MonkeyTypes.DBUser> = { + const newUserDocument: Partial<DBUser> = { name, email, uid, @@ -211,10 +240,7 @@ export async function updateEmail( return true; } -export async function getUser( - uid: string, - stack: string -): Promise<MonkeyTypes.DBUser> { +export async function getUser(uid: string, stack: string): Promise<DBUser> { const user = await getUsersCollection().findOne({ uid }); if (!user) throw new MonkeyError(404, "User not found", stack); return user; @@ -228,11 +254,11 @@ export async function getUser( * @returns partial DBUser only containing requested fields * @throws MonkeyError if user does not exist */ -export async function getPartialUser<K extends keyof MonkeyTypes.DBUser>( +export async function getPartialUser<K extends keyof DBUser>( uid: string, stack: string, fields: K[] -): Promise<Pick<MonkeyTypes.DBUser, K>> { +): Promise<Pick<DBUser, K>> { const projection = new Map(fields.map((it) => [it, 1])); const results = await getUsersCollection().findOne({ uid }, { projection }); if (results === null) throw new MonkeyError(404, "User not found", stack); @@ -240,9 +266,7 @@ export async function getPartialUser<K extends keyof MonkeyTypes.DBUser>( return results; } -export async function findByName( - name: string -): Promise<MonkeyTypes.DBUser | undefined> { +export async function findByName(name: string): Promise<DBUser | undefined> { return ( await getUsersCollection() .find({ name }) @@ -265,7 +289,7 @@ export async function isNameAvailable( export async function getUserByName( name: string, stack: string -): Promise<MonkeyTypes.DBUser> { +): Promise<DBUser> { const user = await findByName(name); if (!user) throw new MonkeyError(404, "User not found", stack); return user; @@ -328,10 +352,7 @@ export async function removeResultFilterPreset( ); } -export async function addTag( - uid: string, - name: string -): Promise<MonkeyTypes.DBUserTag> { +export async function addTag(uid: string, name: string): Promise<DBUserTag> { const toPush = { _id: new ObjectId(), name, @@ -357,7 +378,7 @@ export async function addTag( return toPush; } -export async function getTags(uid: string): Promise<MonkeyTypes.DBUserTag[]> { +export async function getTags(uid: string): Promise<DBUserTag[]> { const user = await getPartialUser(uid, "get tags", ["tags"]); return user.tags ?? []; @@ -426,7 +447,7 @@ export async function updateLbMemory( export async function checkIfPb( uid: string, - user: Pick<MonkeyTypes.DBUser, "personalBests" | "lbPersonalBests">, + user: Pick<DBUser, "personalBests" | "lbPersonalBests">, result: Result ): Promise<boolean> { const { mode } = result; @@ -469,7 +490,7 @@ export async function checkIfPb( export async function checkIfTagPb( uid: string, - user: Pick<MonkeyTypes.DBUser, "tags">, + user: Pick<DBUser, "tags">, result: Result ): Promise<string[]> { if (user.tags === undefined || user.tags.length === 0) { @@ -484,7 +505,7 @@ export async function checkIfTagPb( return []; } - const tagsToCheck: MonkeyTypes.DBUserTag[] = []; + const tagsToCheck: DBUserTag[] = []; user.tags.forEach((userTag) => { for (const resultTag of resultTags ?? []) { if (resultTag === userTag._id.toHexString()) { @@ -571,7 +592,7 @@ export async function linkDiscord( discordId: string, discordAvatar?: string ): Promise<void> { - const updates: Partial<MonkeyTypes.DBUser> = _.pickBy( + const updates: Partial<DBUser> = _.pickBy( { discordId, discordAvatar }, _.identity ); @@ -632,7 +653,7 @@ export async function incrementXp(uid: string, xp: number): Promise<void> { } export async function incrementTestActivity( - user: MonkeyTypes.DBUser, + user: DBUser, timestamp: number ): Promise<void> { if (user.testActivity === undefined) { @@ -719,9 +740,9 @@ export async function editTheme( ); } -export async function getThemes( - uid: string -): Promise<MonkeyTypes.DBCustomTheme[]> { +export type DBCustomTheme = WithObjectId<CustomTheme>; + +export async function getThemes(uid: string): Promise<DBCustomTheme[]> { const user = await getPartialUser(uid, "get themes", ["customThemes"]); return user.customThemes ?? []; } @@ -745,9 +766,7 @@ export async function getPersonalBests( export async function getStats( uid: string -): Promise< - Pick<MonkeyTypes.DBUser, "startedTests" | "completedTests" | "timeTyping"> -> { +): Promise<Pick<DBUser, "startedTests" | "completedTests" | "timeTyping">> { const user = await getPartialUser(uid, "get stats", [ "startedTests", "completedTests", @@ -759,7 +778,7 @@ export async function getStats( export async function getFavoriteQuotes( uid: string -): Promise<NonNullable<MonkeyTypes.DBUser["favoriteQuotes"]>> { +): Promise<NonNullable<DBUser["favoriteQuotes"]>> { const user = await getPartialUser(uid, "get favorite quotes", [ "favoriteQuotes", ]); @@ -843,7 +862,7 @@ export async function recordAutoBanEvent( recentAutoBanTimestamps.push(now); //update user, ban if needed - const updateObj: Partial<MonkeyTypes.DBUser> = { + const updateObj: Partial<DBUser> = { autoBanTimestamps: recentAutoBanTimestamps, }; let banningUser = false; @@ -891,7 +910,7 @@ export async function updateProfile( export async function getInbox( uid: string -): Promise<NonNullable<MonkeyTypes.DBUser["inbox"]>> { +): Promise<NonNullable<DBUser["inbox"]>> { const user = await getPartialUser(uid, "get inbox", ["inbox"]); return user.inbox ?? []; } @@ -981,7 +1000,7 @@ export async function updateInbox( inventory: UserInventory, deletedIds: string[], readIds: string[] - ): Pick<MonkeyTypes.DBUser, "xp" | "inventory" | "inbox"> { + ): Pick<DBUser, "xp" | "inventory" | "inbox"> { const toBeDeleted = inbox.filter((it) => deletedIds.includes(it.id) ); @@ -1121,7 +1140,7 @@ export async function setBanned(uid: string, banned: boolean): Promise<void> { export async function checkIfUserIsPremium( uid: string, - userInfoOverride?: Pick<MonkeyTypes.DBUser, "premium"> + userInfoOverride?: Pick<DBUser, "premium"> ): Promise<boolean> { const premiumFeaturesEnabled = (await getCachedConfiguration(true)).users .premium.enabled; @@ -1141,7 +1160,7 @@ export async function checkIfUserIsPremium( export async function logIpAddress( uid: string, ip: string, - userInfoOverride?: Pick<MonkeyTypes.DBUser, "ips"> + userInfoOverride?: Pick<DBUser, "ips"> ): Promise<void> { const user = userInfoOverride ?? (await getPartialUser(uid, "logIpAddress", ["ips"])); @@ -1165,8 +1184,8 @@ export async function logIpAddress( * @throws MonkeyError if user does not exist */ async function updateUser( - filter: Filter<MonkeyTypes.DBUser>, - update: UpdateFilter<MonkeyTypes.DBUser>, + filter: Filter<DBUser>, + update: UpdateFilter<DBUser>, error: { stack: string; statusCode?: number; message?: string } ): Promise<void> { const result = await getUsersCollection().updateOne(filter, update); diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts index f42aa1bf9..0bd0028ee 100644 --- a/backend/src/middlewares/auth.ts +++ b/backend/src/middlewares/auth.ts @@ -19,7 +19,14 @@ import { RequestAuthenticationOptions, } from "@monkeytype/contracts/schemas/api"; import { Configuration } from "@monkeytype/contracts/schemas/configuration"; -import { getMetadata, TsRestRequestWithCtx } from "./utility"; +import { getMetadata } from "./utility"; +import { TsRestRequestWithContext } from "../api/types"; + +export type DecodedToken = { + type: "Bearer" | "ApeKey" | "None" | "GithubWebhook"; + uid: string; + email: string; +}; const DEFAULT_OPTIONS: RequestAuthenticationOptions = { isGithubWebhook: false, @@ -38,7 +45,7 @@ export function authenticateTsRestRequest< T extends AppRouter | AppRoute >(): TsRestRequestHandler<T> { return async ( - req: TsRestRequestWithCtx, + req: TsRestRequestWithContext, _res: Response, next: NextFunction ): Promise<void> => { @@ -49,7 +56,7 @@ export function authenticateTsRestRequest< }; const startTime = performance.now(); - let token: MonkeyTypes.DecodedToken; + let token: DecodedToken; let authType = "None"; const isPublic = @@ -126,7 +133,7 @@ async function authenticateWithAuthHeader( authHeader: string, configuration: Configuration, options: RequestAuthenticationOptions -): Promise<MonkeyTypes.DecodedToken> { +): Promise<DecodedToken> { const [authScheme, token] = authHeader.split(" "); if (token === undefined) { @@ -158,7 +165,7 @@ async function authenticateWithAuthHeader( async function authenticateWithBearerToken( token: string, options: RequestAuthenticationOptions -): Promise<MonkeyTypes.DecodedToken> { +): Promise<DecodedToken> { try { const decodedToken = await verifyIdToken( token, @@ -221,7 +228,7 @@ async function authenticateWithApeKey( key: string, configuration: Configuration, options: RequestAuthenticationOptions -): Promise<MonkeyTypes.DecodedToken> { +): Promise<DecodedToken> { const isPublic = options.isPublic || (options.isPublicOnDev && isDevEnvironment()); @@ -281,9 +288,7 @@ async function authenticateWithApeKey( } } -async function authenticateWithUid( - token: string -): Promise<MonkeyTypes.DecodedToken> { +async function authenticateWithUid(token: string): Promise<DecodedToken> { if (!isDevEnvironment()) { throw new MonkeyError(401, "Baerer type uid is not supported"); } @@ -301,9 +306,9 @@ async function authenticateWithUid( } export function authenticateGithubWebhook( - req: TsRestRequest, + req: TsRestRequestWithContext, authHeader: string | string[] | undefined -): MonkeyTypes.DecodedToken { +): DecodedToken { try { const webhookSecret = process.env["GITHUB_WEBHOOK_SECRET"]; diff --git a/backend/src/middlewares/configuration.ts b/backend/src/middlewares/configuration.ts index c1990cb46..02b04ecf5 100644 --- a/backend/src/middlewares/configuration.ts +++ b/backend/src/middlewares/configuration.ts @@ -7,13 +7,15 @@ import { ConfigurationPath, RequireConfiguration, } from "@monkeytype/contracts/require-configuration/index"; -import { getMetadata, TsRestRequestWithCtx } from "./utility"; +import { getMetadata } from "./utility"; +import { TsRestRequestWithContext } from "../api/types"; +import { AppRoute, AppRouter } from "@ts-rest/core"; export function verifyRequiredConfiguration< T extends AppRouter | AppRoute >(): TsRestRequestHandler<T> { return async ( - req: TsRestRequestWithCtx, + req: TsRestRequestWithContext, _res: Response, next: NextFunction ): Promise<void> => { diff --git a/backend/src/middlewares/context.ts b/backend/src/middlewares/context.ts index 3a681d0ae..ae76941a1 100644 --- a/backend/src/middlewares/context.ts +++ b/backend/src/middlewares/context.ts @@ -1,5 +1,17 @@ import { getCachedConfiguration } from "../init/configuration"; -import type { Response, NextFunction } from "express"; +import type { + Response, + NextFunction, + Request as ExpressRequest, +} from "express"; +import { DecodedToken } from "./auth"; +import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { ExpressRequestWithContext } from "../api/types"; + +export type Context = { + configuration: Configuration; + decodedToken: DecodedToken; +}; /** * Add the context to the request @@ -8,13 +20,13 @@ import type { Response, NextFunction } from "express"; * @param next */ async function contextMiddleware( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequest, _res: Response, next: NextFunction ): Promise<void> { const configuration = await getCachedConfiguration(true); - req.ctx = { + (req as ExpressRequestWithContext).ctx = { configuration, decodedToken: { type: "None", diff --git a/backend/src/middlewares/error.ts b/backend/src/middlewares/error.ts index 3d45a853c..0d66de7e0 100644 --- a/backend/src/middlewares/error.ts +++ b/backend/src/middlewares/error.ts @@ -14,6 +14,7 @@ import { isDevEnvironment } from "../utils/misc"; import { ObjectId } from "mongodb"; import { version } from "../version"; import { addLog } from "../dal/logs"; +import { ExpressRequestWithContext } from "../api/types"; type DBError = { _id: ObjectId; @@ -34,7 +35,7 @@ type ErrorData = { async function errorHandlingMiddleware( error: Error, - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, res: Response, _next: NextFunction ): Promise<void> { diff --git a/backend/src/middlewares/permission.ts b/backend/src/middlewares/permission.ts index 60b4621aa..77053c100 100644 --- a/backend/src/middlewares/permission.ts +++ b/backend/src/middlewares/permission.ts @@ -1,7 +1,7 @@ import _ from "lodash"; import MonkeyError from "../utils/error"; import type { Response, NextFunction } from "express"; -import { getPartialUser } from "../dal/user"; +import { DBUser, getPartialUser } from "../dal/user"; import { isAdmin } from "../dal/admin-uids"; import { TsRestRequestHandler } from "@ts-rest/express"; import { @@ -10,12 +10,15 @@ import { PermissionId, } from "@monkeytype/contracts/schemas/api"; import { isDevEnvironment } from "../utils/misc"; -import { getMetadata, TsRestRequestWithCtx } from "./utility"; +import { getMetadata } from "./utility"; +import { TsRestRequestWithContext } from "../api/types"; +import { DecodedToken } from "./auth"; +import { AppRoute, AppRouter } from "@ts-rest/core"; type RequestPermissionCheck = { type: "request"; criteria: ( - req: TsRestRequestWithCtx, + req: TsRestRequestWithContext, metadata: EndpointMetadata | undefined ) => Promise<boolean>; invalidMessage?: string; @@ -23,16 +26,16 @@ type RequestPermissionCheck = { type UserPermissionCheck = { type: "user"; - fields: (keyof MonkeyTypes.DBUser)[]; - criteria: (user: MonkeyTypes.DBUser) => boolean; + fields: (keyof DBUser)[]; + criteria: (user: DBUser) => boolean; invalidMessage?: string; }; type PermissionCheck = UserPermissionCheck | RequestPermissionCheck; -function buildUserPermission<K extends keyof MonkeyTypes.DBUser>( +function buildUserPermission<K extends keyof DBUser>( fields: K[], - criteria: (user: Pick<MonkeyTypes.DBUser, K>) => boolean, + criteria: (user: Pick<DBUser, K>) => boolean, invalidMessage?: string ): UserPermissionCheck { return { @@ -73,7 +76,7 @@ export function verifyPermissions< T extends AppRouter | AppRoute >(): TsRestRequestHandler<T> { return async ( - req: TsRestRequestWithCtx, + req: TsRestRequestWithContext, _res: Response, next: NextFunction ): Promise<void> => { @@ -143,7 +146,7 @@ function getRequiredPermissionIds( } async function checkIfUserIsAdmin( - decodedToken: MonkeyTypes.DecodedToken | undefined, + decodedToken: DecodedToken | undefined, options: RequestAuthenticationOptions | undefined ): Promise<boolean> { if (decodedToken === undefined) return false; @@ -162,7 +165,7 @@ type CheckResult = }; async function checkUserPermissions( - decodedToken: MonkeyTypes.DecodedToken | undefined, + decodedToken: DecodedToken | undefined, checks: UserPermissionCheck[] ): Promise<CheckResult> { if (checks === undefined || checks.length === 0) { @@ -181,7 +184,7 @@ async function checkUserPermissions( decodedToken.uid, "check user permissions", checks.flatMap((it) => it.fields) - )) as MonkeyTypes.DBUser; + )) as DBUser; for (const check of checks) { if (!check.criteria(user)) diff --git a/backend/src/middlewares/rate-limit.ts b/backend/src/middlewares/rate-limit.ts index cb158e596..fc5b85b59 100644 --- a/backend/src/middlewares/rate-limit.ts +++ b/backend/src/middlewares/rate-limit.ts @@ -16,12 +16,17 @@ import { Window, } from "@monkeytype/contracts/rate-limit/index"; import statuses from "../constants/monkey-status-codes"; -import { getMetadata, TsRestRequestWithCtx } from "./utility"; +import { getMetadata } from "./utility"; +import { + ExpressRequestWithContext, + TsRestRequestWithContext, +} from "../api/types"; +import { AppRoute, AppRouter } from "@ts-rest/core"; export const REQUEST_MULTIPLIER = isDevEnvironment() ? 100 : 1; export const customHandler = ( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, _res: Response, _next: NextFunction, _options: Options @@ -45,7 +50,7 @@ const getKey = (req: Request, _res: Response): string => { }; const getKeyWithUid = ( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, _res: Response ): string => { const uid = req?.ctx?.decodedToken?.uid; @@ -94,7 +99,7 @@ export function rateLimitRequest< T extends AppRouter | AppRoute >(): TsRestRequestHandler<T> { return async ( - req: TsRestRequestWithCtx, + req: TsRestRequestWithContext, res: Response, next: NextFunction ): Promise<void> => { @@ -149,7 +154,7 @@ const badAuthRateLimiter = new RateLimiterMemory({ }); export async function badAuthRateLimiterHandler( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, res: Response, next: NextFunction ): Promise<void> { @@ -179,7 +184,7 @@ export async function badAuthRateLimiterHandler( } export async function incrementBadAuth( - req: MonkeyTypes.ExpressRequestWithContext, + req: ExpressRequestWithContext, res: Response, status: number ): Promise<void> { diff --git a/backend/src/middlewares/utility.ts b/backend/src/middlewares/utility.ts index 2d29f0180..8f3cbce12 100644 --- a/backend/src/middlewares/utility.ts +++ b/backend/src/middlewares/utility.ts @@ -4,11 +4,7 @@ import { recordClientVersion as prometheusRecordClientVersion } from "../utils/p import { isDevEnvironment } from "../utils/misc"; import MonkeyError from "../utils/error"; import { EndpointMetadata } from "@monkeytype/contracts/schemas/api"; - -export type TsRestRequestWithCtx = { - ctx: Readonly<MonkeyTypes.Context>; -} & TsRestRequest & - ExpressRequest; +import { TsRestRequestWithContext } from "../api/types"; /** * record the client version from the `x-client-version` or ` client-version` header to prometheus @@ -26,8 +22,12 @@ export function recordClientVersion(): RequestHandler { } /** Endpoint is only available in dev environment, else return 503. */ -export function onlyAvailableOnDev(): MonkeyTypes.RequestHandler { - return (_req: TsRestRequestWithCtx, _res: Response, next: NextFunction) => { +export function onlyAvailableOnDev(): RequestHandler { + return ( + _req: TsRestRequestWithContext, + _res: Response, + next: NextFunction + ) => { if (!isDevEnvironment()) { next( new MonkeyError( @@ -41,7 +41,7 @@ export function onlyAvailableOnDev(): MonkeyTypes.RequestHandler { }; } -export function getMetadata(req: TsRestRequestWithCtx): EndpointMetadata { +export function getMetadata(req: TsRestRequestWithContext): EndpointMetadata { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access return (req.tsRestRoute["metadata"] ?? {}) as EndpointMetadata; } diff --git a/backend/src/types/types.d.ts b/backend/src/types/types.d.ts deleted file mode 100644 index d662b0733..000000000 --- a/backend/src/types/types.d.ts +++ /dev/null @@ -1,131 +0,0 @@ -type ObjectId = import("mongodb").ObjectId; - -type ExpressRequest = import("express").Request; -type AppRoute = import("@ts-rest/core").AppRoute; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type TsRestRequest = import("@ts-rest/express").TsRestRequest<any>; -type AppRouter = import("@ts-rest/core").AppRouter; -declare namespace MonkeyTypes { - export type DecodedToken = { - type: "Bearer" | "ApeKey" | "None" | "GithubWebhook"; - uid: string; - email: string; - }; - - export type Context = { - configuration: import("@monkeytype/contracts/schemas/configuration").Configuration; - decodedToken: DecodedToken; - }; - - type ExpressRequestWithContext = { - ctx: Readonly<Context>; - } & ExpressRequest; - - type Request<TQuery = undefined, TBody = undefined, TParams = undefined> = { - query: Readonly<TQuery>; - body: Readonly<TBody>; - params: Readonly<TParams>; - ctx: Readonly<Context>; - raw: Readonly<TsRestRequest>; - }; - - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - type RequestHandler = import("@ts-rest/core").TsRestRequestHandler<any>; - - type DBUser = Omit< - import("@monkeytype/contracts/schemas/users").User, - | "resultFilterPresets" - | "tags" - | "customThemes" - | "isPremium" - | "allTimeLbs" - | "testActivity" - > & { - _id: ObjectId; - resultFilterPresets?: WithObjectId< - import("@monkeytype/contracts/schemas/users").ResultFilters - >[]; - tags?: DBUserTag[]; - lbPersonalBests?: LbPersonalBests; - customThemes?: DBCustomTheme[]; - autoBanTimestamps?: number[]; - inbox?: import("@monkeytype/contracts/schemas/users").MonkeyMail[]; - ips?: string[]; - canReport?: boolean; - lastNameChange?: number; - canManageApeKeys?: boolean; - bananas?: number; - testActivity?: import("@monkeytype/contracts/schemas/users").CountByYearAndDay; - }; - - type DBCustomTheme = WithObjectId< - import("@monkeytype/contracts/schemas/users").CustomTheme - >; - - type DBUserTag = WithObjectId< - import("@monkeytype/contracts/schemas/users").UserTag - >; - - type LbPersonalBests = { - time: Record< - number, - Record< - string, - import("@monkeytype/contracts/schemas/shared").PersonalBest - > - >; - }; - - type WithObjectId<T extends { _id: string }> = Omit<T, "_id"> & { - _id: ObjectId; - }; - - type ApeKeyDB = import("@monkeytype/contracts/schemas/ape-keys").ApeKey & { - _id: ObjectId; - uid: string; - hash: string; - useCount: number; - }; - - type ReportTypes = "quote" | "user"; - - type Report = { - _id: ObjectId; - id: string; - type: ReportTypes; - timestamp: number; - uid: string; - contentId: string; - reason: string; - comment: string; - }; - - type FunboxMetadata = { - name: string; - canGetPb: boolean; - difficultyLevel: number; - properties?: string[]; - frontendForcedConfig?: Record<string, string[] | boolean[]>; - frontendFunctions?: string[]; - }; - - type DBResult = MonkeyTypes.WithObjectId< - import("@monkeytype/contracts/schemas/results").Result< - import("@monkeytype/contracts/schemas/shared").Mode - > - > & { - //legacy values - correctChars?: number; - incorrectChars?: number; - }; - - type BlocklistEntry = { - _id: string; - usernameHash?: string; - emailHash?: string; - discordIdHash?: string; - timestamp: number; - }; - - type DBBlocklistEntry = WithObjectId<MonkeyTypes.BlocklistEntry>; -} diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index e69c47083..d035c9cc6 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -2,6 +2,8 @@ import { MILLISECONDS_IN_DAY } from "@monkeytype/util/date-and-time"; import { roundTo2 } from "@monkeytype/util/numbers"; import _, { omit } from "lodash"; import uaparser from "ua-parser-js"; +import { MonkeyRequest } from "../api/types"; +import { ObjectId } from "mongodb"; //todo split this file into smaller util files (grouped by functionality) @@ -26,14 +28,14 @@ type AgentLog = { device?: string; }; -export function buildAgentLog(req: TsRestRequest): AgentLog { - const agent = uaparser(req.headers["user-agent"]); +export function buildAgentLog(req: MonkeyRequest): AgentLog { + const agent = uaparser(req.raw.headers["user-agent"]); const agentLog: AgentLog = { ip: - (req.headers["cf-connecting-ip"] as string) || - (req.headers["x-forwarded-for"] as string) || - (req.ip as string) || + (req.raw.headers["cf-connecting-ip"] as string) || + (req.raw.headers["x-forwarded-for"] as string) || + (req.raw.ip as string) || "255.255.255.255", agent: `${agent.os.name} ${agent.os.version} ${agent.browser.name} ${agent.browser.version}`, }; @@ -224,3 +226,6 @@ export function replaceObjectIds<T extends { _id: ObjectId }>( if (data === undefined) return data; return data.map((it) => replaceObjectId(it)); } +export type WithObjectId<T extends { _id: string }> = Omit<T, "_id"> & { + _id: ObjectId; +}; diff --git a/backend/src/utils/pb.ts b/backend/src/utils/pb.ts index b20f801cb..e733e25c7 100644 --- a/backend/src/utils/pb.ts +++ b/backend/src/utils/pb.ts @@ -8,10 +8,14 @@ import { } from "@monkeytype/contracts/schemas/shared"; import { Result as ResultType } from "@monkeytype/contracts/schemas/results"; +export type LbPersonalBests = { + time: Record<number, Record<string, PersonalBest>>; +}; + type CheckAndUpdatePbResult = { isPb: boolean; personalBests: PersonalBests; - lbPersonalBests?: MonkeyTypes.LbPersonalBests; + lbPersonalBests?: LbPersonalBests; }; type Result = Omit<ResultType<Mode>, "_id" | "name">; @@ -35,7 +39,7 @@ export function canFunboxGetPb(result: Result): boolean { export function checkAndUpdatePb( userPersonalBests: PersonalBests, - lbPersonalBests: MonkeyTypes.LbPersonalBests | undefined, + lbPersonalBests: LbPersonalBests | undefined, result: Result ): CheckAndUpdatePbResult { const mode = result.mode; @@ -174,9 +178,9 @@ function buildPersonalBest(result: Result): PersonalBest { export function updateLeaderboardPersonalBests( userPersonalBests: PersonalBests, - lbPersonalBests: MonkeyTypes.LbPersonalBests, + lbPersonalBests: LbPersonalBests, result: Result -): MonkeyTypes.LbPersonalBests | null { +): LbPersonalBests | null { if (!shouldUpdateLeaderboardPersonalBests(result)) { return null; } diff --git a/backend/src/utils/result.ts b/backend/src/utils/result.ts index 65abc1c73..53df3dc25 100644 --- a/backend/src/utils/result.ts +++ b/backend/src/utils/result.ts @@ -1,13 +1,21 @@ -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent, Result } from "@monkeytype/contracts/schemas/results"; +import { Mode } from "@monkeytype/contracts/schemas/shared"; import { ObjectId } from "mongodb"; +import { WithObjectId } from "./misc"; + +export type DBResult = WithObjectId<Result<Mode>> & { + //legacy values + correctChars?: number; + incorrectChars?: number; +}; export function buildDbResult( completedEvent: CompletedEvent, userName: string, isPb: boolean -): MonkeyTypes.DBResult { +): DBResult { const ce = completedEvent; - const res: MonkeyTypes.DBResult = { + const res: DBResult = { _id: new ObjectId(), uid: ce.uid, wpm: ce.wpm, @@ -63,9 +71,7 @@ export function buildDbResult( * @param result * @returns */ -export function replaceLegacyValues( - result: MonkeyTypes.DBResult -): MonkeyTypes.DBResult { +export function replaceLegacyValues(result: DBResult): DBResult { //convert legacy values if ( result.correctChars !== undefined && diff --git a/frontend/src/ts/commandline/commandline.ts b/frontend/src/ts/commandline/commandline.ts index 190f77c4a..69c24623b 100644 --- a/frontend/src/ts/commandline/commandline.ts +++ b/frontend/src/ts/commandline/commandline.ts @@ -10,10 +10,11 @@ import * as OutOfFocus from "../test/out-of-focus"; import * as ActivePage from "../states/active-page"; import { focusWords } from "../test/test-ui"; import * as Loader from "../elements/loader"; +import { Command, CommandsSubgroup } from "./types"; type CommandlineMode = "search" | "input"; type InputModeParams = { - command: MonkeyTypes.Command | null; + command: Command | null; placeholder: string | null; value: string | null; icon: string | null; @@ -22,7 +23,7 @@ type InputModeParams = { let activeIndex = 0; let usingSingleList = false; let inputValue = ""; -let activeCommand: MonkeyTypes.Command | null = null; +let activeCommand: Command | null = null; let mouseMode = false; let mode: CommandlineMode = "search"; let inputModeParams: InputModeParams = { @@ -31,7 +32,7 @@ let inputModeParams: InputModeParams = { value: "", icon: "", }; -let subgroupOverride: MonkeyTypes.CommandsSubgroup | null = null; +let subgroupOverride: CommandsSubgroup | null = null; function removeCommandlineBackground(): void { $("#commandLine").addClass("noBackground"); @@ -49,7 +50,7 @@ function addCommandlineBackground(): void { } type ShowSettings = { - subgroupOverride?: MonkeyTypes.CommandsSubgroup | string; + subgroupOverride?: CommandsSubgroup | string; singleListOverride?: boolean; }; @@ -284,9 +285,9 @@ function hideCommands(): void { element.innerHTML = ""; } -let cachedSingleSubgroup: MonkeyTypes.CommandsSubgroup | null = null; +let cachedSingleSubgroup: CommandsSubgroup | null = null; -async function getSubgroup(): Promise<MonkeyTypes.CommandsSubgroup> { +async function getSubgroup(): Promise<CommandsSubgroup> { if (subgroupOverride !== null) { return subgroupOverride; } @@ -302,7 +303,7 @@ async function getSubgroup(): Promise<MonkeyTypes.CommandsSubgroup> { return CommandlineLists.getTopOfStack(); } -async function getList(): Promise<MonkeyTypes.Command[]> { +async function getList(): Promise<Command[]> { return (await getSubgroup()).list; } diff --git a/frontend/src/ts/commandline/lists.ts b/frontend/src/ts/commandline/lists.ts index 011236898..8344bc3b3 100644 --- a/frontend/src/ts/commandline/lists.ts +++ b/frontend/src/ts/commandline/lists.ts @@ -105,6 +105,7 @@ import * as QuoteSearchModal from "../modals/quote-search"; import * as FPSCounter from "../elements/fps-counter"; import { migrateConfig } from "../utils/config"; import { PartialConfigSchema } from "@monkeytype/contracts/schemas/configs"; +import { Command, CommandsSubgroup } from "./types"; const layoutsPromise = JSONData.getLayoutsList(); layoutsPromise @@ -178,7 +179,7 @@ challengesPromise ); }); -export const commands: MonkeyTypes.CommandsSubgroup = { +export const commands: CommandsSubgroup = { title: "", list: [ //result @@ -510,7 +511,7 @@ export function doesListExist(listName: string): boolean { export async function getList( listName: ListsObjectKeys -): Promise<MonkeyTypes.CommandsSubgroup> { +): Promise<CommandsSubgroup> { await Promise.allSettled([ layoutsPromise, languagesPromise, @@ -527,7 +528,7 @@ export async function getList( return list; } -let stack: MonkeyTypes.CommandsSubgroup[] = []; +let stack: CommandsSubgroup[] = []; stack = [commands]; @@ -541,11 +542,11 @@ export function setStackToDefault(): void { setStack([commands]); } -export function setStack(val: MonkeyTypes.CommandsSubgroup[]): void { +export function setStack(val: CommandsSubgroup[]): void { stack = val; } -export function pushToStack(val: MonkeyTypes.CommandsSubgroup): void { +export function pushToStack(val: CommandsSubgroup): void { stack.push(val); } @@ -553,12 +554,12 @@ export function popFromStack(): void { stack.pop(); } -export function getTopOfStack(): MonkeyTypes.CommandsSubgroup { - return stack[stack.length - 1] as MonkeyTypes.CommandsSubgroup; +export function getTopOfStack(): CommandsSubgroup { + return stack[stack.length - 1] as CommandsSubgroup; } -let singleList: MonkeyTypes.CommandsSubgroup | undefined; -export async function getSingleSubgroup(): Promise<MonkeyTypes.CommandsSubgroup> { +let singleList: CommandsSubgroup | undefined; +export async function getSingleSubgroup(): Promise<CommandsSubgroup> { await Promise.allSettled([ layoutsPromise, languagesPromise, @@ -568,7 +569,7 @@ export async function getSingleSubgroup(): Promise<MonkeyTypes.CommandsSubgroup> challengesPromise, ]); - const singleCommands: MonkeyTypes.Command[] = []; + const singleCommands: Command[] = []; for (const command of commands.list) { const ret = buildSingleListCommands(command); singleCommands.push(...ret); @@ -582,10 +583,10 @@ export async function getSingleSubgroup(): Promise<MonkeyTypes.CommandsSubgroup> } function buildSingleListCommands( - command: MonkeyTypes.Command, - parentCommand?: MonkeyTypes.Command -): MonkeyTypes.Command[] { - const commands: MonkeyTypes.Command[] = []; + command: Command, + parentCommand?: Command +): Command[] { + const commands: Command[] = []; if (command.subgroup) { if (command.subgroup.beforeList) { command.subgroup.beforeList(); diff --git a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts index 0cda2388b..13b5567db 100644 --- a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts +++ b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts @@ -1,7 +1,8 @@ import Config, * as UpdateConfig from "../../config"; import { randomTheme } from "../../controllers/theme-controller"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "addThemeToFavorite", display: "Add current theme to favorite", diff --git a/frontend/src/ts/commandline/lists/always-show-decimal.ts b/frontend/src/ts/commandline/lists/always-show-decimal.ts index 20dce01cc..50c138c02 100644 --- a/frontend/src/ts/commandline/lists/always-show-decimal.ts +++ b/frontend/src/ts/commandline/lists/always-show-decimal.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Always show decimal places...", configKey: "alwaysShowDecimalPlaces", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeAlwaysShowDecimal", display: "Always show decimal places...", diff --git a/frontend/src/ts/commandline/lists/background-filter.ts b/frontend/src/ts/commandline/lists/background-filter.ts index 3e7fc3961..31b9ccab8 100644 --- a/frontend/src/ts/commandline/lists/background-filter.ts +++ b/frontend/src/ts/commandline/lists/background-filter.ts @@ -1,6 +1,7 @@ import Config, * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Custom background filter...", configKey: "customBackgroundFilter", list: [ @@ -67,7 +68,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setCustomBackgroundFilter", display: "Custom background filter...", diff --git a/frontend/src/ts/commandline/lists/background-size.ts b/frontend/src/ts/commandline/lists/background-size.ts index a3fb5ec3d..d8e29a17d 100644 --- a/frontend/src/ts/commandline/lists/background-size.ts +++ b/frontend/src/ts/commandline/lists/background-size.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Custom background size...", configKey: "customBackgroundSize", list: [ @@ -34,7 +35,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setCustomBackgroundSize", display: "Custom background size...", diff --git a/frontend/src/ts/commandline/lists/bail-out.ts b/frontend/src/ts/commandline/lists/bail-out.ts index a478d4281..d73fbdae9 100644 --- a/frontend/src/ts/commandline/lists/bail-out.ts +++ b/frontend/src/ts/commandline/lists/bail-out.ts @@ -3,6 +3,7 @@ import * as CustomText from "../../test/custom-text"; import * as TestLogic from "../../test/test-logic"; import * as TestState from "../../test/test-state"; import * as CustomTextState from "../../states/custom-text-name"; +import { Command, CommandsSubgroup } from "../types"; function canBailOut(): boolean { return ( @@ -23,7 +24,7 @@ function canBailOut(): boolean { ); } -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Are you sure...", list: [ { @@ -47,7 +48,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "bailOut", display: "Bail out...", diff --git a/frontend/src/ts/commandline/lists/blind-mode.ts b/frontend/src/ts/commandline/lists/blind-mode.ts index 720fa8389..34febb6c5 100644 --- a/frontend/src/ts/commandline/lists/blind-mode.ts +++ b/frontend/src/ts/commandline/lists/blind-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Blind mode...", configKey: "blindMode", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeBlindMode", display: "Blind mode...", diff --git a/frontend/src/ts/commandline/lists/british-english.ts b/frontend/src/ts/commandline/lists/british-english.ts index ea7433aba..2e7a5119f 100644 --- a/frontend/src/ts/commandline/lists/british-english.ts +++ b/frontend/src/ts/commandline/lists/british-english.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "British english...", configKey: "britishEnglish", list: [ @@ -26,7 +27,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeBritishEnglish", display: "British english...", diff --git a/frontend/src/ts/commandline/lists/caps-lock-warning.ts b/frontend/src/ts/commandline/lists/caps-lock-warning.ts index 370fe49c2..092cf858c 100644 --- a/frontend/src/ts/commandline/lists/caps-lock-warning.ts +++ b/frontend/src/ts/commandline/lists/caps-lock-warning.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Caps lock warning...", configKey: "capsLockWarning", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "capsLockWarning", display: "Caps lock warning...", diff --git a/frontend/src/ts/commandline/lists/caret-style.ts b/frontend/src/ts/commandline/lists/caret-style.ts index 4eef78a25..dff3d53f7 100644 --- a/frontend/src/ts/commandline/lists/caret-style.ts +++ b/frontend/src/ts/commandline/lists/caret-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Caret style...", configKey: "caretStyle", list: [ @@ -65,7 +66,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeCaretStyle", display: "Caret style...", diff --git a/frontend/src/ts/commandline/lists/colorful-mode.ts b/frontend/src/ts/commandline/lists/colorful-mode.ts index d5a197529..6523212ee 100644 --- a/frontend/src/ts/commandline/lists/colorful-mode.ts +++ b/frontend/src/ts/commandline/lists/colorful-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Colorful mode...", configKey: "colorfulMode", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeColorfulMode", display: "Colorful mode...", diff --git a/frontend/src/ts/commandline/lists/confidence-mode.ts b/frontend/src/ts/commandline/lists/confidence-mode.ts index a163378c5..c1cd11186 100644 --- a/frontend/src/ts/commandline/lists/confidence-mode.ts +++ b/frontend/src/ts/commandline/lists/confidence-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeConfidenceMode", display: "Confidence mode...", diff --git a/frontend/src/ts/commandline/lists/custom-theme.ts b/frontend/src/ts/commandline/lists/custom-theme.ts index 34a47b2ae..897151c66 100644 --- a/frontend/src/ts/commandline/lists/custom-theme.ts +++ b/frontend/src/ts/commandline/lists/custom-theme.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Custom theme", configKey: "customTheme", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setCustomTheme", display: "Custom theme...", diff --git a/frontend/src/ts/commandline/lists/custom-themes-list.ts b/frontend/src/ts/commandline/lists/custom-themes-list.ts index 89bb2dc07..ca6aae004 100644 --- a/frontend/src/ts/commandline/lists/custom-themes-list.ts +++ b/frontend/src/ts/commandline/lists/custom-themes-list.ts @@ -2,15 +2,16 @@ import * as UpdateConfig from "../../config"; import { isAuthenticated } from "../../firebase"; import * as DB from "../../db"; import * as ThemeController from "../../controllers/theme-controller"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Custom themes list...", // configKey: "customThemeId", beforeList: (): void => update(), list: [], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setCustomThemeId", display: "Custom themes...", diff --git a/frontend/src/ts/commandline/lists/difficulty.ts b/frontend/src/ts/commandline/lists/difficulty.ts index b0a16756c..a454e8a69 100644 --- a/frontend/src/ts/commandline/lists/difficulty.ts +++ b/frontend/src/ts/commandline/lists/difficulty.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Difficulty...", configKey: "difficulty", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeDifficulty", display: "Difficulty...", diff --git a/frontend/src/ts/commandline/lists/enable-ads.ts b/frontend/src/ts/commandline/lists/enable-ads.ts index f7bc8fdd1..50cbdc556 100644 --- a/frontend/src/ts/commandline/lists/enable-ads.ts +++ b/frontend/src/ts/commandline/lists/enable-ads.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Set enable ads...", configKey: "ads", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setEnableAds", display: "Enable ads...", diff --git a/frontend/src/ts/commandline/lists/flip-test-colors.ts b/frontend/src/ts/commandline/lists/flip-test-colors.ts index 58ca1c0a4..ad719155f 100644 --- a/frontend/src/ts/commandline/lists/flip-test-colors.ts +++ b/frontend/src/ts/commandline/lists/flip-test-colors.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Flip test colors...", configKey: "flipTestColors", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeFlipTestColors", display: "Flip test colors...", diff --git a/frontend/src/ts/commandline/lists/font-family.ts b/frontend/src/ts/commandline/lists/font-family.ts index e6f500b20..4131b7068 100644 --- a/frontend/src/ts/commandline/lists/font-family.ts +++ b/frontend/src/ts/commandline/lists/font-family.ts @@ -1,13 +1,15 @@ import * as UpdateConfig from "../../config"; import * as UI from "../../ui"; +import { FontObject } from "../../utils/json-data"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Font family...", configKey: "fontFamily", list: [], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeFontFamily", display: "Font family...", @@ -16,7 +18,7 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(fonts: MonkeyTypes.FontObject[]): void { +function update(fonts: FontObject[]): void { fonts.forEach((font) => { const configVal = font.name.replace(/ /g, "_"); diff --git a/frontend/src/ts/commandline/lists/font-size.ts b/frontend/src/ts/commandline/lists/font-size.ts index c4706d626..b55b277f7 100644 --- a/frontend/src/ts/commandline/lists/font-size.ts +++ b/frontend/src/ts/commandline/lists/font-size.ts @@ -1,6 +1,7 @@ import Config, * as UpdateConfig from "../../config"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeFontSize", display: "Font size...", diff --git a/frontend/src/ts/commandline/lists/freedom-mode.ts b/frontend/src/ts/commandline/lists/freedom-mode.ts index 3f634319e..6563c57ab 100644 --- a/frontend/src/ts/commandline/lists/freedom-mode.ts +++ b/frontend/src/ts/commandline/lists/freedom-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Freedom mode...", configKey: "freedomMode", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeFreedomMode", display: "Freedom mode...", diff --git a/frontend/src/ts/commandline/lists/funbox.ts b/frontend/src/ts/commandline/lists/funbox.ts index d63b62800..704d0d4d3 100644 --- a/frontend/src/ts/commandline/lists/funbox.ts +++ b/frontend/src/ts/commandline/lists/funbox.ts @@ -3,8 +3,10 @@ import * as TestLogic from "../../test/test-logic"; import * as ManualRestart from "../../test/manual-restart-tracker"; import Config from "../../config"; import { areFunboxesCompatible } from "../../test/funbox/funbox-validation"; +import { FunboxMetadata } from "../../utils/json-data"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Funbox...", configKey: "funbox", list: [ @@ -22,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeFunbox", display: "Funbox...", @@ -32,7 +34,7 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(funboxes: MonkeyTypes.FunboxMetadata[]): void { +function update(funboxes: FunboxMetadata[]): void { subgroup.list = []; subgroup.list.push({ id: "changeFunboxNone", diff --git a/frontend/src/ts/commandline/lists/hide-extra-letters.ts b/frontend/src/ts/commandline/lists/hide-extra-letters.ts index da423ac39..f0a0274e3 100644 --- a/frontend/src/ts/commandline/lists/hide-extra-letters.ts +++ b/frontend/src/ts/commandline/lists/hide-extra-letters.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Hide extra letters...", configKey: "hideExtraLetters", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeHideExtraLetters", display: "Hide extra letters...", diff --git a/frontend/src/ts/commandline/lists/highlight-mode.ts b/frontend/src/ts/commandline/lists/highlight-mode.ts index 0626fa506..568b9acdc 100644 --- a/frontend/src/ts/commandline/lists/highlight-mode.ts +++ b/frontend/src/ts/commandline/lists/highlight-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Highlight mode...", configKey: "highlightMode", list: [ @@ -55,7 +56,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeHighlightMode", display: "Highlight mode...", diff --git a/frontend/src/ts/commandline/lists/indicate-typos.ts b/frontend/src/ts/commandline/lists/indicate-typos.ts index e0c3f3665..f103a414a 100644 --- a/frontend/src/ts/commandline/lists/indicate-typos.ts +++ b/frontend/src/ts/commandline/lists/indicate-typos.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Indicate typos...", configKey: "indicateTypos", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeIndicateTypos", display: "Indicate typos...", diff --git a/frontend/src/ts/commandline/lists/key-tips.ts b/frontend/src/ts/commandline/lists/key-tips.ts index 2e752c84f..a2c09e29d 100644 --- a/frontend/src/ts/commandline/lists/key-tips.ts +++ b/frontend/src/ts/commandline/lists/key-tips.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Key tips...", configKey: "showKeyTips", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeyTips", display: "Key tips...", diff --git a/frontend/src/ts/commandline/lists/keymap-layouts.ts b/frontend/src/ts/commandline/lists/keymap-layouts.ts index 17118501e..69bb4dd60 100644 --- a/frontend/src/ts/commandline/lists/keymap-layouts.ts +++ b/frontend/src/ts/commandline/lists/keymap-layouts.ts @@ -1,8 +1,10 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { LayoutsList } from "../../utils/json-data"; import { capitalizeFirstLetterOfEachWord } from "../../utils/strings"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change keymap layout...", configKey: "keymapLayout", list: [ @@ -13,7 +15,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeymapLayout", display: "Keymap layout...", @@ -23,7 +25,7 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(layouts: MonkeyTypes.Layouts): void { +function update(layouts: LayoutsList): void { subgroup.list = []; subgroup.list.push({ id: "changeKeymapLayoutOverrideSync", diff --git a/frontend/src/ts/commandline/lists/keymap-legend-style.ts b/frontend/src/ts/commandline/lists/keymap-legend-style.ts index a050e0859..d609b593e 100644 --- a/frontend/src/ts/commandline/lists/keymap-legend-style.ts +++ b/frontend/src/ts/commandline/lists/keymap-legend-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Keymap legend style...", configKey: "keymapLegendStyle", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeymapLegendStyle", display: "Keymap legend style...", diff --git a/frontend/src/ts/commandline/lists/keymap-mode.ts b/frontend/src/ts/commandline/lists/keymap-mode.ts index d92bbbcde..62fcc0b55 100644 --- a/frontend/src/ts/commandline/lists/keymap-mode.ts +++ b/frontend/src/ts/commandline/lists/keymap-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Keymap mode...", configKey: "keymapMode", list: [ @@ -40,7 +41,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "toggleKeymap", display: "Keymap mode...", diff --git a/frontend/src/ts/commandline/lists/keymap-show-top-row.ts b/frontend/src/ts/commandline/lists/keymap-show-top-row.ts index 0e8c06833..65559dc65 100644 --- a/frontend/src/ts/commandline/lists/keymap-show-top-row.ts +++ b/frontend/src/ts/commandline/lists/keymap-show-top-row.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Keymap show top row...", configKey: "keymapShowTopRow", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeymapShowTopRow", display: "Keymap show top row...", diff --git a/frontend/src/ts/commandline/lists/keymap-size.ts b/frontend/src/ts/commandline/lists/keymap-size.ts index eb0f4e0ca..fd67a1e74 100644 --- a/frontend/src/ts/commandline/lists/keymap-size.ts +++ b/frontend/src/ts/commandline/lists/keymap-size.ts @@ -1,6 +1,7 @@ import Config, * as UpdateConfig from "../../config"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeymapSize", display: "Keymap size...", diff --git a/frontend/src/ts/commandline/lists/keymap-style.ts b/frontend/src/ts/commandline/lists/keymap-style.ts index 61768a7bd..9b01e9018 100644 --- a/frontend/src/ts/commandline/lists/keymap-style.ts +++ b/frontend/src/ts/commandline/lists/keymap-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Keymap style...", configKey: "keymapStyle", list: [ @@ -63,7 +64,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeKeymapStyle", display: "Keymap style...", diff --git a/frontend/src/ts/commandline/lists/languages.ts b/frontend/src/ts/commandline/lists/languages.ts index 01b95dab2..c1a3a9d54 100644 --- a/frontend/src/ts/commandline/lists/languages.ts +++ b/frontend/src/ts/commandline/lists/languages.ts @@ -3,8 +3,9 @@ import { capitalizeFirstLetterOfEachWord, getLanguageDisplayString, } from "../../utils/strings"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Language...", configKey: "language", list: [ @@ -15,7 +16,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLanguage", display: "Language...", diff --git a/frontend/src/ts/commandline/lists/layouts.ts b/frontend/src/ts/commandline/lists/layouts.ts index df1c83dc0..5f9b01595 100644 --- a/frontend/src/ts/commandline/lists/layouts.ts +++ b/frontend/src/ts/commandline/lists/layouts.ts @@ -1,8 +1,10 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { LayoutsList } from "../../utils/json-data"; import { capitalizeFirstLetterOfEachWord } from "../../utils/strings"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Layout emulator...", configKey: "layout", list: [ @@ -13,7 +15,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLayout", display: "Layout emulator...", @@ -22,7 +24,7 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(layouts: MonkeyTypes.Layouts): void { +function update(layouts: LayoutsList): void { subgroup.list = []; subgroup.list.push({ id: "changeLayoutDefault", diff --git a/frontend/src/ts/commandline/lists/lazy-mode.ts b/frontend/src/ts/commandline/lists/lazy-mode.ts index 9d1694a52..d886aae0e 100644 --- a/frontend/src/ts/commandline/lists/lazy-mode.ts +++ b/frontend/src/ts/commandline/lists/lazy-mode.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Lazy mode...", configKey: "lazyMode", list: [ @@ -26,7 +27,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLazyMode", display: "Lazy mode...", diff --git a/frontend/src/ts/commandline/lists/live-acc-style.ts b/frontend/src/ts/commandline/lists/live-acc-style.ts index 30e3d4f14..a82db9c75 100644 --- a/frontend/src/ts/commandline/lists/live-acc-style.ts +++ b/frontend/src/ts/commandline/lists/live-acc-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live acc style...", configKey: "liveAccStyle", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLiveAccStyle", display: "Live acc style...", diff --git a/frontend/src/ts/commandline/lists/live-burst-style.ts b/frontend/src/ts/commandline/lists/live-burst-style.ts index 9af83a78a..a56d122a6 100644 --- a/frontend/src/ts/commandline/lists/live-burst-style.ts +++ b/frontend/src/ts/commandline/lists/live-burst-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live burst style...", configKey: "liveBurstStyle", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLiveBurstStyle", display: "Live burst style...", diff --git a/frontend/src/ts/commandline/lists/live-speed-style.ts b/frontend/src/ts/commandline/lists/live-speed-style.ts index b3a5914de..bb42153cf 100644 --- a/frontend/src/ts/commandline/lists/live-speed-style.ts +++ b/frontend/src/ts/commandline/lists/live-speed-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live speed style...", configKey: "liveSpeedStyle", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeLiveSpeedStyle", display: "Live speed style...", diff --git a/frontend/src/ts/commandline/lists/load-challenge.ts b/frontend/src/ts/commandline/lists/load-challenge.ts index e260ff2db..c9ad3a7dc 100644 --- a/frontend/src/ts/commandline/lists/load-challenge.ts +++ b/frontend/src/ts/commandline/lists/load-challenge.ts @@ -2,13 +2,15 @@ import { navigate } from "../../controllers/route-controller"; import * as ChallengeController from "../../controllers/challenge-controller"; import * as TestLogic from "../../test/test-logic"; import { capitalizeFirstLetterOfEachWord } from "../../utils/strings"; +import { Command, CommandsSubgroup } from "../types"; +import { Challenge } from "../../utils/json-data"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Load challenge...", list: [], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "loadChallenge", display: "Load challenge...", @@ -17,7 +19,7 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(challenges: MonkeyTypes.Challenge[]): void { +function update(challenges: Challenge[]): void { challenges.forEach((challenge) => { subgroup.list.push({ id: "loadChallenge" + capitalizeFirstLetterOfEachWord(challenge.name), diff --git a/frontend/src/ts/commandline/lists/max-line-width.ts b/frontend/src/ts/commandline/lists/max-line-width.ts index ad97d6335..63ab65adb 100644 --- a/frontend/src/ts/commandline/lists/max-line-width.ts +++ b/frontend/src/ts/commandline/lists/max-line-width.ts @@ -1,6 +1,7 @@ import Config, * as UpdateConfig from "../../config"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeMaxLineWidth", display: "Max line width...", diff --git a/frontend/src/ts/commandline/lists/min-acc.ts b/frontend/src/ts/commandline/lists/min-acc.ts index 84466ef8e..0b831d99e 100644 --- a/frontend/src/ts/commandline/lists/min-acc.ts +++ b/frontend/src/ts/commandline/lists/min-acc.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change min accuracy mode...", configKey: "minAcc", list: [ @@ -26,7 +27,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeMinAcc", display: "Minimum accuracy...", diff --git a/frontend/src/ts/commandline/lists/min-burst.ts b/frontend/src/ts/commandline/lists/min-burst.ts index 0ecd089ba..626d0a734 100644 --- a/frontend/src/ts/commandline/lists/min-burst.ts +++ b/frontend/src/ts/commandline/lists/min-burst.ts @@ -1,7 +1,8 @@ import Config, * as UpdateConfig from "../../config"; import { get as getTypingSpeedUnit } from "../../utils/typing-speed-units"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change min burst mode...", configKey: "minBurst", list: [ @@ -44,7 +45,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeMinBurst", display: "Minimum burst...", diff --git a/frontend/src/ts/commandline/lists/min-wpm.ts b/frontend/src/ts/commandline/lists/min-wpm.ts index 67a03f0e1..5322ff28c 100644 --- a/frontend/src/ts/commandline/lists/min-wpm.ts +++ b/frontend/src/ts/commandline/lists/min-wpm.ts @@ -1,7 +1,8 @@ import Config, * as UpdateConfig from "../../config"; import { get as getTypingSpeedUnit } from "../../utils/typing-speed-units"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change min speed mode...", configKey: "minWpm", list: [ @@ -30,7 +31,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeMinWpm", display: "Minimum speed...", diff --git a/frontend/src/ts/commandline/lists/mode.ts b/frontend/src/ts/commandline/lists/mode.ts index 78a8fd8d9..844aff159 100644 --- a/frontend/src/ts/commandline/lists/mode.ts +++ b/frontend/src/ts/commandline/lists/mode.ts @@ -1,8 +1,9 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; import * as ManualRestart from "../../test/manual-restart-tracker"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeMode", display: "Mode...", diff --git a/frontend/src/ts/commandline/lists/monkey-power-level.ts b/frontend/src/ts/commandline/lists/monkey-power-level.ts index da530873e..4bf907e95 100644 --- a/frontend/src/ts/commandline/lists/monkey-power-level.ts +++ b/frontend/src/ts/commandline/lists/monkey-power-level.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Power mode...", configKey: "monkeyPowerLevel", list: [ @@ -37,7 +38,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "monkeyPower", display: "Power mode...", diff --git a/frontend/src/ts/commandline/lists/navigation.ts b/frontend/src/ts/commandline/lists/navigation.ts index d9d7e1460..9750419dd 100644 --- a/frontend/src/ts/commandline/lists/navigation.ts +++ b/frontend/src/ts/commandline/lists/navigation.ts @@ -1,8 +1,9 @@ import { navigate } from "../../controllers/route-controller"; import { isAuthenticated } from "../../firebase"; import { toggleFullscreen } from "../../utils/misc"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "viewTypingPage", display: "View Typing Page", diff --git a/frontend/src/ts/commandline/lists/numbers.ts b/frontend/src/ts/commandline/lists/numbers.ts index 26cee7546..77bb86262 100644 --- a/frontend/src/ts/commandline/lists/numbers.ts +++ b/frontend/src/ts/commandline/lists/numbers.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Numbers...", configKey: "numbers", list: [ @@ -26,7 +27,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeNumbers", display: "Numbers...", diff --git a/frontend/src/ts/commandline/lists/opposite-shift-mode.ts b/frontend/src/ts/commandline/lists/opposite-shift-mode.ts index 507ec9627..215923a81 100644 --- a/frontend/src/ts/commandline/lists/opposite-shift-mode.ts +++ b/frontend/src/ts/commandline/lists/opposite-shift-mode.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as ModesNotice from "./../../elements/modes-notice"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change opposite shift mode...", configKey: "oppositeShiftMode", list: [ @@ -35,7 +36,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeOppositeShiftMode", display: "Change opposite shift mode...", diff --git a/frontend/src/ts/commandline/lists/out-of-focus-warning.ts b/frontend/src/ts/commandline/lists/out-of-focus-warning.ts index 36c3e0fa7..14a1d491d 100644 --- a/frontend/src/ts/commandline/lists/out-of-focus-warning.ts +++ b/frontend/src/ts/commandline/lists/out-of-focus-warning.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Colorful mode...", configKey: "showOutOfFocusWarning", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeOutOfFocusWarning", display: "Out of focus warning...", diff --git a/frontend/src/ts/commandline/lists/pace-caret-style.ts b/frontend/src/ts/commandline/lists/pace-caret-style.ts index f7db6e19a..92e70d497 100644 --- a/frontend/src/ts/commandline/lists/pace-caret-style.ts +++ b/frontend/src/ts/commandline/lists/pace-caret-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change pace caret style...", configKey: "paceCaretStyle", list: [ @@ -65,7 +66,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changePaceCaretStyle", display: "Pace caret style...", diff --git a/frontend/src/ts/commandline/lists/pace-caret.ts b/frontend/src/ts/commandline/lists/pace-caret.ts index 98be56512..c7b2d9b66 100644 --- a/frontend/src/ts/commandline/lists/pace-caret.ts +++ b/frontend/src/ts/commandline/lists/pace-caret.ts @@ -1,8 +1,9 @@ import Config, * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; import { get as getTypingSpeedUnit } from "../../utils/typing-speed-units"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Pace caret mode...", configKey: "paceCaret", list: [ @@ -78,7 +79,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changePaceCaret", display: "Pace caret mode...", diff --git a/frontend/src/ts/commandline/lists/presets.ts b/frontend/src/ts/commandline/lists/presets.ts index 0f2b8843c..552440a6f 100644 --- a/frontend/src/ts/commandline/lists/presets.ts +++ b/frontend/src/ts/commandline/lists/presets.ts @@ -4,8 +4,9 @@ import * as Settings from "../../pages/settings"; import * as PresetController from "../../controllers/preset-controller"; import * as EditPresetPopup from "../../modals/edit-preset"; import { isAuthenticated } from "../../firebase"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Presets...", list: [], beforeList: (): void => { @@ -13,7 +14,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { }, }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { visible: false, id: "applyPreset", @@ -30,7 +31,7 @@ function update(): void { const snapshot = DB.getSnapshot(); subgroup.list = []; if (!snapshot?.presets || snapshot.presets.length === 0) return; - snapshot.presets.forEach((preset: MonkeyTypes.SnapshotPreset) => { + snapshot.presets.forEach((preset) => { const dis = preset.display; subgroup.list.push({ diff --git a/frontend/src/ts/commandline/lists/punctuation.ts b/frontend/src/ts/commandline/lists/punctuation.ts index 4bef072ca..d99fc528f 100644 --- a/frontend/src/ts/commandline/lists/punctuation.ts +++ b/frontend/src/ts/commandline/lists/punctuation.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changePunctuation", display: "Punctuation...", diff --git a/frontend/src/ts/commandline/lists/quick-end.ts b/frontend/src/ts/commandline/lists/quick-end.ts index 4ff21385f..409d5b37a 100644 --- a/frontend/src/ts/commandline/lists/quick-end.ts +++ b/frontend/src/ts/commandline/lists/quick-end.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Quick end...", configKey: "quickEnd", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeQuickEnd", display: "Quick end...", diff --git a/frontend/src/ts/commandline/lists/quick-restart.ts b/frontend/src/ts/commandline/lists/quick-restart.ts index 803a16fbf..7cab7f657 100644 --- a/frontend/src/ts/commandline/lists/quick-restart.ts +++ b/frontend/src/ts/commandline/lists/quick-restart.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Quick restart...", configKey: "quickRestart", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeQuickRestart", display: "Quick restart...", diff --git a/frontend/src/ts/commandline/lists/quote-favorites.ts b/frontend/src/ts/commandline/lists/quote-favorites.ts index 5a859a0bb..38149266b 100644 --- a/frontend/src/ts/commandline/lists/quote-favorites.ts +++ b/frontend/src/ts/commandline/lists/quote-favorites.ts @@ -1,12 +1,13 @@ import Config from "../../config"; -import QuotesController from "../../controllers/quotes-controller"; +import QuotesController, { Quote } from "../../controllers/quotes-controller"; import * as Notifications from "../../elements/notifications"; import { isAuthenticated } from "../../firebase"; import { createErrorMessage } from "../../utils/misc"; import * as Loader from "../../elements/loader"; import * as TestWords from "../../test/test-words"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "addQuoteToFavorite", display: "Add current quote to favorite", @@ -24,7 +25,7 @@ const commands: MonkeyTypes.Command[] = [ try { Loader.show(); await QuotesController.setQuoteFavorite( - TestWords.currentQuote as MonkeyTypes.QuoteWithTextSplit, + TestWords.currentQuote as Quote, true ); Loader.hide(); @@ -56,7 +57,7 @@ const commands: MonkeyTypes.Command[] = [ try { Loader.show(); await QuotesController.setQuoteFavorite( - TestWords.currentQuote as MonkeyTypes.QuoteWithTextSplit, + TestWords.currentQuote as Quote, false ); Loader.hide(); diff --git a/frontend/src/ts/commandline/lists/quote-length.ts b/frontend/src/ts/commandline/lists/quote-length.ts index 7b1f5fdde..49b2b7c03 100644 --- a/frontend/src/ts/commandline/lists/quote-length.ts +++ b/frontend/src/ts/commandline/lists/quote-length.ts @@ -1,8 +1,9 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; import { isAuthenticated } from "../../firebase"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeQuoteLength", display: "Quote length...", diff --git a/frontend/src/ts/commandline/lists/random-theme.ts b/frontend/src/ts/commandline/lists/random-theme.ts index c2b29cae7..2fba39b80 100644 --- a/frontend/src/ts/commandline/lists/random-theme.ts +++ b/frontend/src/ts/commandline/lists/random-theme.ts @@ -1,8 +1,9 @@ import * as UpdateConfig from "../../config"; import { isAuthenticated } from "../../firebase"; import * as Notifications from "../../elements/notifications"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Random theme...", configKey: "randomTheme", list: [ @@ -64,7 +65,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeRandomTheme", display: "Random theme...", diff --git a/frontend/src/ts/commandline/lists/repeat-quotes.ts b/frontend/src/ts/commandline/lists/repeat-quotes.ts index 3265f9e44..fc788350f 100644 --- a/frontend/src/ts/commandline/lists/repeat-quotes.ts +++ b/frontend/src/ts/commandline/lists/repeat-quotes.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Repeat quotes...", configKey: "repeatQuotes", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeRepeatQuotes", display: "Repeat quotes...", diff --git a/frontend/src/ts/commandline/lists/repeated-pace.ts b/frontend/src/ts/commandline/lists/repeated-pace.ts index d4eab1042..b1b44ce91 100644 --- a/frontend/src/ts/commandline/lists/repeated-pace.ts +++ b/frontend/src/ts/commandline/lists/repeated-pace.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Repeated pace...", configKey: "repeatedPace", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeRepeatedPace", display: "Repeated pace...", diff --git a/frontend/src/ts/commandline/lists/result-saving.ts b/frontend/src/ts/commandline/lists/result-saving.ts index 9ff3913e9..acf1d9600 100644 --- a/frontend/src/ts/commandline/lists/result-saving.ts +++ b/frontend/src/ts/commandline/lists/result-saving.ts @@ -1,7 +1,8 @@ import * as TestState from "../../test/test-state"; import * as ModesNotice from "../../elements/modes-notice"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Result saving...", list: [ { @@ -25,7 +26,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "setResultSaving", display: "Result saving...", diff --git a/frontend/src/ts/commandline/lists/result-screen.ts b/frontend/src/ts/commandline/lists/result-screen.ts index 9c5cc6a02..2c9e3ae66 100644 --- a/frontend/src/ts/commandline/lists/result-screen.ts +++ b/frontend/src/ts/commandline/lists/result-screen.ts @@ -6,8 +6,9 @@ import * as TestInput from "../../test/test-input"; import * as TestWords from "../../test/test-words"; import Config from "../../config"; import * as PractiseWords from "../../test/practise-words"; +import { Command, CommandsSubgroup } from "../types"; -const practiceSubgroup: MonkeyTypes.CommandsSubgroup = { +const practiceSubgroup: CommandsSubgroup = { title: "Practice words...", list: [ { @@ -43,7 +44,7 @@ const practiceSubgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "nextTest", display: "Next test", diff --git a/frontend/src/ts/commandline/lists/show-all-lines.ts b/frontend/src/ts/commandline/lists/show-all-lines.ts index e631fc47d..7b463bca9 100644 --- a/frontend/src/ts/commandline/lists/show-all-lines.ts +++ b/frontend/src/ts/commandline/lists/show-all-lines.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Show all lines...", configKey: "showAllLines", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeShowAllLines", display: "Show all lines...", diff --git a/frontend/src/ts/commandline/lists/show-average.ts b/frontend/src/ts/commandline/lists/show-average.ts index f2738d3b9..5d89a09cd 100644 --- a/frontend/src/ts/commandline/lists/show-average.ts +++ b/frontend/src/ts/commandline/lists/show-average.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Show average...", configKey: "showAverage", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeShowAverage", display: "Show average...", diff --git a/frontend/src/ts/commandline/lists/show-words-history.ts b/frontend/src/ts/commandline/lists/show-words-history.ts index 0fa479ae3..59e4a7046 100644 --- a/frontend/src/ts/commandline/lists/show-words-history.ts +++ b/frontend/src/ts/commandline/lists/show-words-history.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Always show words history...", configKey: "alwaysShowWordsHistory", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeShowWordsHistory", display: "Always show words history...", diff --git a/frontend/src/ts/commandline/lists/single-list-commandline.ts b/frontend/src/ts/commandline/lists/single-list-commandline.ts index 6f4723ea5..cfaa97905 100644 --- a/frontend/src/ts/commandline/lists/single-list-commandline.ts +++ b/frontend/src/ts/commandline/lists/single-list-commandline.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Single list command line...", configKey: "singleListCommandLine", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "singleListCommandLine", display: "Single list command line...", diff --git a/frontend/src/ts/commandline/lists/smooth-caret.ts b/frontend/src/ts/commandline/lists/smooth-caret.ts index 437ad7191..d3cea716d 100644 --- a/frontend/src/ts/commandline/lists/smooth-caret.ts +++ b/frontend/src/ts/commandline/lists/smooth-caret.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Smooth caret...", configKey: "smoothCaret", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeSmoothCaret", display: "Smooth caret...", diff --git a/frontend/src/ts/commandline/lists/smooth-line-scroll.ts b/frontend/src/ts/commandline/lists/smooth-line-scroll.ts index 6f167f147..feed43c45 100644 --- a/frontend/src/ts/commandline/lists/smooth-line-scroll.ts +++ b/frontend/src/ts/commandline/lists/smooth-line-scroll.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Smooth line scroll...", configKey: "smoothLineScroll", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeSmoothLineScroll", display: "Smooth line scroll...", diff --git a/frontend/src/ts/commandline/lists/sound-on-click.ts b/frontend/src/ts/commandline/lists/sound-on-click.ts index fb85cf7ee..187299ce5 100644 --- a/frontend/src/ts/commandline/lists/sound-on-click.ts +++ b/frontend/src/ts/commandline/lists/sound-on-click.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as SoundController from "../../controllers/sound-controller"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Sound on click...", configKey: "playSoundOnClick", list: [ @@ -196,7 +197,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeSoundOnClick", display: "Sound on click...", diff --git a/frontend/src/ts/commandline/lists/sound-on-error.ts b/frontend/src/ts/commandline/lists/sound-on-error.ts index c04f017fb..c6c6e53ed 100644 --- a/frontend/src/ts/commandline/lists/sound-on-error.ts +++ b/frontend/src/ts/commandline/lists/sound-on-error.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as SoundController from "../../controllers/sound-controller"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Sound on error...", configKey: "playSoundOnError", list: [ @@ -64,7 +65,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeSoundOnError", display: "Sound on error...", diff --git a/frontend/src/ts/commandline/lists/sound-volume.ts b/frontend/src/ts/commandline/lists/sound-volume.ts index 2cac406b8..7009ec34c 100644 --- a/frontend/src/ts/commandline/lists/sound-volume.ts +++ b/frontend/src/ts/commandline/lists/sound-volume.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as SoundController from "../../controllers/sound-controller"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Sound volume...", configKey: "soundVolume", list: [ @@ -45,7 +46,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeSoundVolume", display: "Sound volume...", diff --git a/frontend/src/ts/commandline/lists/start-graphs-at-zero.ts b/frontend/src/ts/commandline/lists/start-graphs-at-zero.ts index 9ecd2d3c4..780bc42e1 100644 --- a/frontend/src/ts/commandline/lists/start-graphs-at-zero.ts +++ b/frontend/src/ts/commandline/lists/start-graphs-at-zero.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Start graphs at zero...", configKey: "startGraphsAtZero", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeStartGraphsAtZero", display: "Start graphs at zero...", diff --git a/frontend/src/ts/commandline/lists/stop-on-error.ts b/frontend/src/ts/commandline/lists/stop-on-error.ts index d0f8cf5df..e979f5cf6 100644 --- a/frontend/src/ts/commandline/lists/stop-on-error.ts +++ b/frontend/src/ts/commandline/lists/stop-on-error.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeStopOnError", display: "Stop on error...", diff --git a/frontend/src/ts/commandline/lists/strict-space.ts b/frontend/src/ts/commandline/lists/strict-space.ts index 36d1f1d03..2dd822eb5 100644 --- a/frontend/src/ts/commandline/lists/strict-space.ts +++ b/frontend/src/ts/commandline/lists/strict-space.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Strict space...", configKey: "strictSpace", list: [ @@ -23,7 +24,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeStrictSpace", display: "Strict space...", diff --git a/frontend/src/ts/commandline/lists/tags.ts b/frontend/src/ts/commandline/lists/tags.ts index 377f2fc86..52d789987 100644 --- a/frontend/src/ts/commandline/lists/tags.ts +++ b/frontend/src/ts/commandline/lists/tags.ts @@ -5,8 +5,9 @@ import * as TagController from "../../controllers/tag-controller"; import Config from "../../config"; import * as PaceCaret from "../../test/pace-caret"; import { isAuthenticated } from "../../firebase"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Change tags...", list: [], beforeList: (): void => { @@ -14,7 +15,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { }, }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTags", display: "Tags...", diff --git a/frontend/src/ts/commandline/lists/tape-mode.ts b/frontend/src/ts/commandline/lists/tape-mode.ts index 26577f0d6..7b7f09d08 100644 --- a/frontend/src/ts/commandline/lists/tape-mode.ts +++ b/frontend/src/ts/commandline/lists/tape-mode.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Tape mode...", configKey: "tapeMode", list: [ @@ -31,7 +32,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTapeMode", display: "Tape mode...", diff --git a/frontend/src/ts/commandline/lists/themes.ts b/frontend/src/ts/commandline/lists/themes.ts index 58bc1de05..ad3a05532 100644 --- a/frontend/src/ts/commandline/lists/themes.ts +++ b/frontend/src/ts/commandline/lists/themes.ts @@ -1,14 +1,16 @@ import Config, * as UpdateConfig from "../../config"; import { capitalizeFirstLetterOfEachWord } from "../../utils/strings"; import * as ThemeController from "../../controllers/theme-controller"; +import { Command, CommandsSubgroup } from "../types"; +import { Theme } from "../../utils/json-data"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Theme...", configKey: "theme", list: [], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTheme", display: "Theme...", @@ -17,9 +19,9 @@ const commands: MonkeyTypes.Command[] = [ }, ]; -function update(themes: MonkeyTypes.Theme[]): void { +function update(themes: Theme[]): void { subgroup.list = []; - const favs: MonkeyTypes.Command[] = []; + const favs: Command[] = []; themes.forEach((theme) => { if (Config.favThemes.includes(theme.name)) { favs.push({ diff --git a/frontend/src/ts/commandline/lists/time.ts b/frontend/src/ts/commandline/lists/time.ts index 34416071e..f96b5d88f 100644 --- a/frontend/src/ts/commandline/lists/time.ts +++ b/frontend/src/ts/commandline/lists/time.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTimeConfig", display: "Time...", diff --git a/frontend/src/ts/commandline/lists/timer-color.ts b/frontend/src/ts/commandline/lists/timer-color.ts index ee272a3d2..cdeb6ac50 100644 --- a/frontend/src/ts/commandline/lists/timer-color.ts +++ b/frontend/src/ts/commandline/lists/timer-color.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live stats color...", configKey: "timerColor", list: [ @@ -43,7 +44,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTimerColor", display: "Live stats color...", diff --git a/frontend/src/ts/commandline/lists/timer-opacity.ts b/frontend/src/ts/commandline/lists/timer-opacity.ts index 38897fdce..365cd0272 100644 --- a/frontend/src/ts/commandline/lists/timer-opacity.ts +++ b/frontend/src/ts/commandline/lists/timer-opacity.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live stats opacity...", configKey: "timerOpacity", list: [ @@ -43,7 +44,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTimerOpacity", display: "Live stats opacity...", diff --git a/frontend/src/ts/commandline/lists/timer-style.ts b/frontend/src/ts/commandline/lists/timer-style.ts index b79c6ee02..0c3c33b45 100644 --- a/frontend/src/ts/commandline/lists/timer-style.ts +++ b/frontend/src/ts/commandline/lists/timer-style.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Live progress style...", configKey: "timerStyle", list: [ @@ -39,7 +40,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTimerStyle", display: "Live progress style...", diff --git a/frontend/src/ts/commandline/lists/typing-speed-unit.ts b/frontend/src/ts/commandline/lists/typing-speed-unit.ts index d2884e058..abcd68c33 100644 --- a/frontend/src/ts/commandline/lists/typing-speed-unit.ts +++ b/frontend/src/ts/commandline/lists/typing-speed-unit.ts @@ -1,6 +1,7 @@ import * as UpdateConfig from "../../config"; +import { Command, CommandsSubgroup } from "../types"; -const subgroup: MonkeyTypes.CommandsSubgroup = { +const subgroup: CommandsSubgroup = { title: "Typing speed unit...", configKey: "typingSpeedUnit", list: [ @@ -48,7 +49,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { ], }; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeTypingSpeedUnit", display: "Typing speed unit...", diff --git a/frontend/src/ts/commandline/lists/words.ts b/frontend/src/ts/commandline/lists/words.ts index b780f722e..154f10b09 100644 --- a/frontend/src/ts/commandline/lists/words.ts +++ b/frontend/src/ts/commandline/lists/words.ts @@ -1,7 +1,8 @@ import * as UpdateConfig from "../../config"; import * as TestLogic from "../../test/test-logic"; +import { Command } from "../types"; -const commands: MonkeyTypes.Command[] = [ +const commands: Command[] = [ { id: "changeWordCount", display: "Words...", diff --git a/frontend/src/ts/commandline/types.ts b/frontend/src/ts/commandline/types.ts new file mode 100644 index 000000000..4932b274d --- /dev/null +++ b/frontend/src/ts/commandline/types.ts @@ -0,0 +1,42 @@ +import { Config } from "@monkeytype/contracts/schemas/configs"; +import AnimatedModal from "../utils/animated-modal"; + +// this file is needed becauase otherwise it would produce a circular dependency + +export type CommandExecOptions = { + input?: string; + commandlineModal: AnimatedModal; +}; + +export type Command = { + id: string; + display: string; + singleListDisplay?: string; + singleListDisplayNoIcon?: string; + subgroup?: CommandsSubgroup; + found?: boolean; + icon?: string; + sticky?: boolean; + alias?: string; + input?: boolean; + visible?: boolean; + customStyle?: string; + opensModal?: boolean; + defaultValue?: () => string; + configKey?: keyof Config; + configValue?: string | number | boolean | number[]; + configValueMode?: "include"; + exec?: (options: CommandExecOptions) => void; + hover?: () => void; + available?: () => boolean; + active?: () => boolean; + shouldFocusTestUI?: boolean; + customData?: Record<string, string | boolean>; +}; + +export type CommandsSubgroup = { + title: string; + configKey?: keyof Config; + list: Command[]; + beforeList?: () => void; +}; diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts index 381b1c7a9..441f7078e 100644 --- a/frontend/src/ts/config.ts +++ b/frontend/src/ts/config.ts @@ -1831,7 +1831,7 @@ export function setCustomBackground( } export async function setCustomLayoutfluid( - value: MonkeyTypes.CustomLayoutFluidSpaces, + value: ConfigSchemas.CustomLayoutFluid, nosave?: boolean ): Promise<boolean> { const trimmed = value.trim(); @@ -1932,7 +1932,7 @@ export function setBurstHeatmap(value: boolean, nosave?: boolean): boolean { } export async function apply( - configToApply: Config | MonkeyTypes.ConfigChanges + configToApply: Config | Partial<Config> ): Promise<void> { if (configToApply === undefined) return; @@ -2064,8 +2064,8 @@ export async function loadFromLocalStorage(): Promise<void> { loadDone(); } -export function getConfigChanges(): MonkeyTypes.ConfigChanges { - const configChanges: MonkeyTypes.ConfigChanges = {}; +export function getConfigChanges(): Partial<Config> { + const configChanges: Partial<Config> = {}; typedKeys(config) .filter((key) => { return config[key] !== DefaultConfig[key]; diff --git a/frontend/src/ts/constants/default-snapshot.ts b/frontend/src/ts/constants/default-snapshot.ts index 28737c9c1..0f26c07db 100644 --- a/frontend/src/ts/constants/default-snapshot.ts +++ b/frontend/src/ts/constants/default-snapshot.ts @@ -1,6 +1,7 @@ +import { deepClone } from "../utils/misc"; import defaultConfig from "./default-config"; -export const defaultSnap: MonkeyTypes.Snapshot = { +const defaultSnap = { results: undefined, personalBests: { time: {}, @@ -42,3 +43,5 @@ export const defaultSnap: MonkeyTypes.Snapshot = { }, }, }; + +export default deepClone(defaultSnap); diff --git a/frontend/src/ts/controllers/account-controller.ts b/frontend/src/ts/controllers/account-controller.ts index cbcee49f3..78291adf4 100644 --- a/frontend/src/ts/controllers/account-controller.ts +++ b/frontend/src/ts/controllers/account-controller.ts @@ -122,7 +122,7 @@ async function getDataAndInit(): Promise<boolean> { LoadingPage.updateBar(45); } LoadingPage.updateText("Applying settings..."); - const snapshot = DB.getSnapshot() as MonkeyTypes.Snapshot; + const snapshot = DB.getSnapshot() as DB.Snapshot; AccountButton.update(snapshot); Alerts.setNotificationBubbleVisible(snapshot.inboxUnreadSize > 0); showFavoriteQuoteLength(); diff --git a/frontend/src/ts/controllers/badge-controller.ts b/frontend/src/ts/controllers/badge-controller.ts index 1bea8ad6d..79b137d36 100644 --- a/frontend/src/ts/controllers/badge-controller.ts +++ b/frontend/src/ts/controllers/badge-controller.ts @@ -1,4 +1,14 @@ -const badges: Record<number, MonkeyTypes.UserBadge> = { +type UserBadge = { + id: number; + name: string; + description: string; + icon?: string; + background?: string; + color?: string; + customStyle?: string; +}; + +const badges: Record<number, UserBadge> = { 1: { id: 1, name: "Developer", @@ -119,7 +129,7 @@ export function getHTMLById( noBalloon = false, showUnknown = false ): string { - const badge = badges[id] as MonkeyTypes.UserBadge | undefined; + const badge = badges[id] as UserBadge | undefined; if (!badge && !showUnknown) { return ""; @@ -160,6 +170,6 @@ export function getHTMLById( }</div>`; } -export function getById(id: number): MonkeyTypes.UserBadge | undefined { +export function getById(id: number): UserBadge | undefined { return badges[id]; } diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts index c64826130..87acaa697 100644 --- a/frontend/src/ts/controllers/chart-controller.ts +++ b/frontend/src/ts/controllers/chart-controller.ts @@ -269,11 +269,41 @@ export const result = new ChartWithUpdateColors< export let accountHistoryActiveIndex: number; +export type HistoryChartData = { + x: number; + y: number; + wpm: number; + acc: number; + mode: string; + mode2: string; + punctuation: boolean; + language: string; + timestamp: number; + difficulty: string; + raw: number; + isPb: boolean; +}; + +export type AccChartData = { + x: number; + y: number; + errorRate: number; +}; + +export type OtherChartData = { + x: number; + y: number; +}; + +export type ActivityChartDataPoint = { + x: number; + y: number; + amount?: number; +}; + export const accountHistory = new ChartWithUpdateColors< "line", - | MonkeyTypes.HistoryChartData[] - | MonkeyTypes.AccChartData[] - | MonkeyTypes.OtherChartData[], + HistoryChartData[] | AccChartData[] | OtherChartData[], string, | "wpm" | "pb" @@ -500,14 +530,14 @@ export const accountHistory = new ChartWithUpdateColors< if (tooltipItem.datasetIndex !== 0) { const resultData = tooltipItem.dataset.data[ tooltipItem.dataIndex - ] as MonkeyTypes.AccChartData; + ] as AccChartData; return `error rate: ${Numbers.roundTo2( resultData.errorRate )}%\nacc: ${Numbers.roundTo2(100 - resultData.errorRate)}%`; } const resultData = tooltipItem.dataset.data[ tooltipItem.dataIndex - ] as MonkeyTypes.HistoryChartData; + ] as HistoryChartData; let label = `${Config.typingSpeedUnit}: ${resultData.wpm}` + "\n" + @@ -559,7 +589,7 @@ export const accountHistory = new ChartWithUpdateColors< export const accountActivity = new ChartWithUpdateColors< "bar" | "line", - MonkeyTypes.ActivityChartDataPoint[], + ActivityChartDataPoint[], string, "count" | "avgWpm" >( @@ -670,13 +700,13 @@ export const accountActivity = new ChartWithUpdateColors< const firstItem = tooltipItem[0] as TooltipItem<"bar" | "line">; const resultData = firstItem.dataset.data[ firstItem.dataIndex - ] as MonkeyTypes.ActivityChartDataPoint; + ] as ActivityChartDataPoint; return format(new Date(resultData.x), "dd MMM yyyy"); }, beforeLabel: function (tooltipItem): string { const resultData = tooltipItem.dataset.data[ tooltipItem.dataIndex - ] as MonkeyTypes.ActivityChartDataPoint; + ] as ActivityChartDataPoint; switch (tooltipItem.datasetIndex) { case 0: return `Time Typing: ${DateTime.secondsToString( @@ -704,7 +734,7 @@ export const accountActivity = new ChartWithUpdateColors< export const accountHistogram = new ChartWithUpdateColors< "bar", - MonkeyTypes.ActivityChartDataPoint[], + ActivityChartDataPoint[], string, "count" >( @@ -807,7 +837,7 @@ export const accountHistogram = new ChartWithUpdateColors< export const globalSpeedHistogram = new ChartWithUpdateColors< "bar", - MonkeyTypes.ActivityChartDataPoint[], + ActivityChartDataPoint[], string, "count" >( @@ -1102,9 +1132,9 @@ function updateAverage100(updateChart = true): void { async function updateColors< TType extends ChartType = "bar" | "line" | "scatter", TData = - | MonkeyTypes.HistoryChartData[] - | MonkeyTypes.AccChartData[] - | MonkeyTypes.ActivityChartDataPoint[] + | HistoryChartData[] + | AccChartData[] + | ActivityChartDataPoint[] | number[], TLabel = string >(chart: ChartWithUpdateColors<TType, TData, TLabel>): Promise<void> { diff --git a/frontend/src/ts/controllers/page-controller.ts b/frontend/src/ts/controllers/page-controller.ts index e38309051..1e90ccd11 100644 --- a/frontend/src/ts/controllers/page-controller.ts +++ b/frontend/src/ts/controllers/page-controller.ts @@ -14,6 +14,7 @@ import * as PageAccountSettings from "../pages/account-settings"; import * as PageTransition from "../states/page-transition"; import * as AdController from "../controllers/ad-controller"; import * as Focus from "../test/focus"; +import { PageName } from "../pages/page"; type ChangeOptions = { force?: boolean; @@ -22,7 +23,7 @@ type ChangeOptions = { }; export async function change( - pageName: MonkeyTypes.PageName, + pageName: PageName, options = {} as ChangeOptions ): Promise<boolean> { const defaultOptions = { diff --git a/frontend/src/ts/controllers/preset-controller.ts b/frontend/src/ts/controllers/preset-controller.ts index 1198ec40c..b8afa1cfd 100644 --- a/frontend/src/ts/controllers/preset-controller.ts +++ b/frontend/src/ts/controllers/preset-controller.ts @@ -41,7 +41,7 @@ export async function apply(_id: string): Promise<void> { }); UpdateConfig.saveFullConfigToLocalStorage(); } -function isPartialPreset(preset: MonkeyTypes.SnapshotPreset): boolean { +function isPartialPreset(preset: DB.SnapshotPreset): boolean { return preset.settingGroups !== undefined && preset.settingGroups !== null; } diff --git a/frontend/src/ts/controllers/quotes-controller.ts b/frontend/src/ts/controllers/quotes-controller.ts index 71bc025b9..a0cbacdc0 100644 --- a/frontend/src/ts/controllers/quotes-controller.ts +++ b/frontend/src/ts/controllers/quotes-controller.ts @@ -5,25 +5,38 @@ import { subscribe } from "../observables/config-event"; import * as DB from "../db"; import Ape from "../ape"; -type JsonQuote = { +export type Quote = { text: string; britishText?: string; source: string; length: number; id: number; + group: number; + language: string; + textSplit?: string[]; +}; + +export type QuoteWithTextSplit = Quote & { + textSplit: string[]; }; type QuoteData = { language: string; - quotes: JsonQuote[]; + quotes: { + text: string; + britishText?: string; + source: string; + length: number; + id: number; + }[]; groups: [number, number][]; }; type QuoteCollection = { - quotes: MonkeyTypes.Quote[]; + quotes: Quote[]; length: number; language: string | null; - groups: MonkeyTypes.Quote[][]; + groups: Quote[][]; }; const defaultQuoteCollection: QuoteCollection = { @@ -36,7 +49,7 @@ const defaultQuoteCollection: QuoteCollection = { class QuotesController { private quoteCollection: QuoteCollection = defaultQuoteCollection; - private quoteQueue: MonkeyTypes.Quote[] = []; + private quoteQueue: Quote[] = []; private queueIndex = 0; async getQuotes( @@ -71,8 +84,8 @@ class QuotesController { }; // Transform JSON Quote schema to MonkeyTypes Quote schema - data.quotes.forEach((quote: JsonQuote) => { - const monkeyTypeQuote: MonkeyTypes.Quote = { + data.quotes.forEach((quote) => { + const monkeyTypeQuote: Quote = { text: quote.text, britishText: quote.britishText, source: quote.source, @@ -107,12 +120,10 @@ class QuotesController { return this.quoteCollection; } - getQuoteById(id: number): MonkeyTypes.Quote | undefined { - const targetQuote = this.quoteCollection.quotes.find( - (quote: MonkeyTypes.Quote) => { - return quote.id === id; - } - ); + getQuoteById(id: number): Quote | undefined { + const targetQuote = this.quoteCollection.quotes.find((quote: Quote) => { + return quote.id === id; + }); return targetQuote; } @@ -133,7 +144,7 @@ class QuotesController { this.queueIndex = 0; } - getRandomQuote(): MonkeyTypes.Quote | null { + getRandomQuote(): Quote | null { if (this.quoteQueue.length === 0) { return null; } @@ -143,14 +154,14 @@ class QuotesController { shuffle(this.quoteQueue); } - const randomQuote = this.quoteQueue[this.queueIndex] as MonkeyTypes.Quote; + const randomQuote = this.quoteQueue[this.queueIndex] as Quote; this.queueIndex += 1; return randomQuote; } - getRandomFavoriteQuote(language: string): MonkeyTypes.Quote | null { + getRandomFavoriteQuote(language: string): Quote | null { const snapshot = DB.getSnapshot(); if (!snapshot) { return null; @@ -182,7 +193,7 @@ class QuotesController { return randomQuote ?? null; } - isQuoteFavorite({ language: quoteLanguage, id }: MonkeyTypes.Quote): boolean { + isQuoteFavorite({ language: quoteLanguage, id }: Quote): boolean { const snapshot = DB.getSnapshot(); if (!snapshot) { return false; @@ -206,10 +217,7 @@ class QuotesController { return matchedLanguage !== undefined; } - async setQuoteFavorite( - quote: MonkeyTypes.Quote, - isFavorite: boolean - ): Promise<void> { + async setQuoteFavorite(quote: Quote, isFavorite: boolean): Promise<void> { const snapshot = DB.getSnapshot(); if (!snapshot) { throw new Error("Snapshot is not available"); diff --git a/frontend/src/ts/db.ts b/frontend/src/ts/db.ts index b1618839a..18f9dabd2 100644 --- a/frontend/src/ts/db.ts +++ b/frontend/src/ts/db.ts @@ -3,7 +3,6 @@ import * as Notifications from "./elements/notifications"; import * as LoadingPage from "./pages/loading"; import DefaultConfig from "./constants/default-config"; import { isAuthenticated } from "./firebase"; -import { defaultSnap } from "./constants/default-snapshot"; import * as ConnectionState from "./states/connection"; import { lastElementFromArray } from "./utils/arrays"; import { getFunboxList } from "./utils/json-data"; @@ -15,7 +14,14 @@ import { } from "./elements/test-activity-calendar"; import * as Loader from "./elements/loader"; -import { Badge } from "@monkeytype/contracts/schemas/users"; +import { + Badge, + CustomTheme, + ResultFilters, + User, + UserProfileDetails, + UserTag, +} from "@monkeytype/contracts/schemas/users"; import { Config, Difficulty } from "@monkeytype/contracts/schemas/configs"; import { Mode, @@ -23,8 +29,86 @@ import { PersonalBest, PersonalBests, } from "@monkeytype/contracts/schemas/shared"; - -let dbSnapshot: MonkeyTypes.Snapshot | undefined; +import { Preset } from "@monkeytype/contracts/schemas/presets"; +import defaultSnapshot from "./constants/default-snapshot"; +import { Result } from "@monkeytype/contracts/schemas/results"; + +export type SnapshotUserTag = UserTag & { + active?: boolean; + display: string; +}; + +export type SnapshotResult<M extends Mode> = Omit< + Result<M>, + | "_id" + | "bailedOut" + | "blindMode" + | "lazyMode" + | "difficulty" + | "funbox" + | "language" + | "numbers" + | "punctuation" + | "quoteLength" + | "restartCount" + | "incompleteTestSeconds" + | "afkDuration" + | "tags" +> & { + _id: string; + bailedOut: boolean; + blindMode: boolean; + lazyMode: boolean; + difficulty: string; + funbox: string; + language: string; + numbers: boolean; + punctuation: boolean; + quoteLength: number; + restartCount: number; + incompleteTestSeconds: number; + afkDuration: number; + tags: string[]; +}; + +export type Snapshot = Omit< + User, + | "timeTyping" + | "startedTests" + | "completedTests" + | "profileDetails" + | "streak" + | "resultFilterPresets" + | "tags" + | "xp" + | "testActivity" +> & { + typingStats: { + timeTyping: number; + startedTests: number; + completedTests: number; + }; + details?: UserProfileDetails; + inboxUnreadSize: number; + streak: number; + maxStreak: number; + filterPresets: ResultFilters[]; + isPremium: boolean; + streakHourOffset?: number; + config: Config; + tags: SnapshotUserTag[]; + presets: SnapshotPreset[]; + results?: SnapshotResult<Mode>[]; + xp: number; + testActivity?: ModifiableTestActivityCalendar; + testActivityByYear?: { [key: string]: TestActivityCalendar }; +}; + +export type SnapshotPreset = Preset & { + display: string; +}; + +let dbSnapshot: Snapshot | undefined; export class SnapshotInitError extends Error { constructor(message: string, public responseCode: number) { @@ -34,13 +118,11 @@ export class SnapshotInitError extends Error { } } -export function getSnapshot(): MonkeyTypes.Snapshot | undefined { +export function getSnapshot(): Snapshot | undefined { return dbSnapshot; } -export function setSnapshot( - newSnapshot: MonkeyTypes.Snapshot | undefined -): void { +export function setSnapshot(newSnapshot: Snapshot | undefined): void { const originalBanned = dbSnapshot?.banned; const originalVerified = dbSnapshot?.verified; const lbOptOut = dbSnapshot?.lbOptOut; @@ -63,11 +145,9 @@ export function setSnapshot( } } -export async function initSnapshot(): Promise< - MonkeyTypes.Snapshot | number | boolean -> { +export async function initSnapshot(): Promise<Snapshot | number | boolean> { //send api request with token that returns tags, presets, and data needed for snap - const snap = { ...defaultSnap }; + const snap = defaultSnapshot as Snapshot; try { if (!isAuthenticated()) return false; // if (ActivePage.get() === "loading") { @@ -241,24 +321,26 @@ export async function initSnapshot(): Promise< ...preset, display: preset.name.replace(/_/gi, " "), }; - }) as MonkeyTypes.SnapshotPreset[]; + }) as SnapshotPreset[]; snap.presets = presetsWithDisplay; - snap.presets = snap.presets?.sort((a, b) => { - if (a.name > b.name) { - return 1; - } else if (a.name < b.name) { - return -1; - } else { - return 0; + snap.presets = snap.presets?.sort( + (a: SnapshotPreset, b: SnapshotPreset) => { + if (a.name > b.name) { + return 1; + } else if (a.name < b.name) { + return -1; + } else { + return 0; + } } - }); + ); } dbSnapshot = snap; return dbSnapshot; } catch (e) { - dbSnapshot = defaultSnap; + dbSnapshot = defaultSnapshot; throw e; } } @@ -290,29 +372,27 @@ export async function getUserResults(offset?: number): Promise<boolean> { return false; } - const results: MonkeyTypes.FullResult<Mode>[] = response.body.data.map( - (result) => { - if (result.bailedOut === undefined) result.bailedOut = false; - if (result.blindMode === undefined) result.blindMode = false; - if (result.lazyMode === undefined) result.lazyMode = false; - if (result.difficulty === undefined) result.difficulty = "normal"; - if (result.funbox === undefined) result.funbox = "none"; - if (result.language === undefined || result.language === null) { - result.language = "english"; - } - if (result.numbers === undefined) result.numbers = false; - if (result.punctuation === undefined) result.punctuation = false; - if (result.numbers === undefined) result.numbers = false; - if (result.quoteLength === undefined) result.quoteLength = -1; - if (result.restartCount === undefined) result.restartCount = 0; - if (result.incompleteTestSeconds === undefined) { - result.incompleteTestSeconds = 0; - } - if (result.afkDuration === undefined) result.afkDuration = 0; - if (result.tags === undefined) result.tags = []; - return result as MonkeyTypes.FullResult<Mode>; + const results: SnapshotResult<Mode>[] = response.body.data.map((result) => { + if (result.bailedOut === undefined) result.bailedOut = false; + if (result.blindMode === undefined) result.blindMode = false; + if (result.lazyMode === undefined) result.lazyMode = false; + if (result.difficulty === undefined) result.difficulty = "normal"; + if (result.funbox === undefined) result.funbox = "none"; + if (result.language === undefined || result.language === null) { + result.language = "english"; } - ); + if (result.numbers === undefined) result.numbers = false; + if (result.punctuation === undefined) result.punctuation = false; + if (result.numbers === undefined) result.numbers = false; + if (result.quoteLength === undefined) result.quoteLength = -1; + if (result.restartCount === undefined) result.restartCount = 0; + if (result.incompleteTestSeconds === undefined) { + result.incompleteTestSeconds = 0; + } + if (result.afkDuration === undefined) result.afkDuration = 0; + if (result.tags === undefined) result.tags = []; + return result as SnapshotResult<Mode>; + }); results?.sort((a, b) => b.timestamp - a.timestamp); if (dbSnapshot.results !== undefined && dbSnapshot.results.length > 0) { @@ -329,14 +409,12 @@ export async function getUserResults(offset?: number): Promise<boolean> { return true; } -function _getCustomThemeById( - themeID: string -): MonkeyTypes.CustomTheme | undefined { +function _getCustomThemeById(themeID: string): CustomTheme | undefined { return dbSnapshot?.customThemes?.find((t) => t._id === themeID); } export async function addCustomTheme( - theme: MonkeyTypes.RawCustomTheme + theme: Omit<CustomTheme, "_id"> ): Promise<boolean> { if (!dbSnapshot) return false; @@ -363,7 +441,7 @@ export async function addCustomTheme( return false; } - const newCustomTheme: MonkeyTypes.CustomTheme = { + const newCustomTheme: CustomTheme = { ...theme, _id: response.body.data._id, }; @@ -374,7 +452,7 @@ export async function addCustomTheme( export async function editCustomTheme( themeId: string, - newTheme: MonkeyTypes.RawCustomTheme + newTheme: Omit<CustomTheme, "_id"> ): Promise<boolean> { if (!isAuthenticated()) return false; if (!dbSnapshot) return false; @@ -403,7 +481,7 @@ export async function editCustomTheme( return false; } - const newCustomTheme: MonkeyTypes.CustomTheme = { + const newCustomTheme: CustomTheme = { ...newTheme, _id: themeId, }; @@ -476,7 +554,7 @@ export async function getUserAverage10<M extends Mode>( (result.lazyMode === lazyMode || (result.lazyMode === undefined && !lazyMode)) && (activeTagIds.length === 0 || - activeTagIds.some((tagId) => result.tags.includes(tagId))) + activeTagIds.some((tagId) => result.tags?.includes(tagId))) ) { // Continue if the mode2 doesn't match and it's not a quote if ( @@ -556,7 +634,7 @@ export async function getUserDailyBest<M extends Mode>( (result.lazyMode === lazyMode || (result.lazyMode === undefined && !lazyMode)) && (activeTagIds.length === 0 || - activeTagIds.some((tagId) => result.tags.includes(tagId))) + activeTagIds.some((tagId) => result.tags?.includes(tagId))) ) { if (result.timestamp < Date.now() - 86400000) { continue; @@ -797,7 +875,7 @@ export async function saveLocalTagPB<M extends Mode>( function cont(): void { const filteredtag = dbSnapshot?.tags?.filter( (t) => t._id === tagId - )[0] as MonkeyTypes.UserTag; + )[0] as SnapshotUserTag; filteredtag.personalBests ??= { time: {}, @@ -945,7 +1023,7 @@ export async function resetConfig(): Promise<void> { } } -export function saveLocalResult(result: MonkeyTypes.FullResult<Mode>): void { +export function saveLocalResult(result: SnapshotResult<Mode>): void { const snapshot = getSnapshot(); if (!snapshot) return; @@ -1018,7 +1096,7 @@ export function setStreak(streak: number): void { export async function getTestActivityCalendar( yearString: string -): Promise<MonkeyTypes.TestActivityCalendar | undefined> { +): Promise<TestActivityCalendar | undefined> { if (!isAuthenticated() || dbSnapshot === undefined) return undefined; if (yearString === "current") return dbSnapshot.testActivity; diff --git a/frontend/src/ts/elements/account-button.ts b/frontend/src/ts/elements/account-button.ts index be7085f19..80e2fe6f4 100644 --- a/frontend/src/ts/elements/account-button.ts +++ b/frontend/src/ts/elements/account-button.ts @@ -9,6 +9,7 @@ import { } from "../controllers/user-flag-controller"; import { isAuthenticated } from "../firebase"; import { mapRange } from "@monkeytype/util/numbers"; +import { Snapshot } from "../db"; let usingAvatar = false; @@ -154,11 +155,10 @@ export function updateAvatar( } } -export function update(snapshot: MonkeyTypes.Snapshot | undefined): void { +export function update(snapshot: Snapshot | undefined): void { if (isAuthenticated()) { // this function is called after the snapshot is loaded (awaited), so it should be fine - const { xp, discordId, discordAvatar, name } = - snapshot as MonkeyTypes.Snapshot; + const { xp, discordId, discordAvatar, name } = snapshot as Snapshot; updateName(name); updateFlags(snapshot ?? {}); diff --git a/frontend/src/ts/elements/account/result-filters.ts b/frontend/src/ts/elements/account/result-filters.ts index 0c647bb41..d29c61a93 100644 --- a/frontend/src/ts/elements/account/result-filters.ts +++ b/frontend/src/ts/elements/account/result-filters.ts @@ -269,7 +269,7 @@ function setAllFilters(group: ResultFiltersGroup, value: boolean): void { }); } -export function loadTags(tags: MonkeyTypes.UserTag[]): void { +export function loadTags(tags: DB.SnapshotUserTag[]): void { tags.forEach((tag) => { defaultResultFilters.tags[tag._id] = true; }); @@ -601,14 +601,14 @@ $(".pageAccount .topFilters button.currentConfigFilter").on("click", () => { filters.mode[Config.mode] = true; if (Config.mode === "time") { if ([15, 30, 60, 120].includes(Config.time)) { - const configTime = Config.time as MonkeyTypes.DefaultTimeModes; + const configTime = `${Config.time}` as keyof typeof filters.time; filters.time[configTime] = true; } else { filters.time.custom = true; } } else if (Config.mode === "words") { if ([10, 25, 50, 100, 200].includes(Config.words)) { - const configWords = Config.words as MonkeyTypes.DefaultWordsModes; + const configWords = `${Config.words}` as keyof typeof filters.words; filters.words[configWords] = true; } else { filters.words.custom = true; diff --git a/frontend/src/ts/elements/alerts.ts b/frontend/src/ts/elements/alerts.ts index f48a2cfcf..beeac7356 100644 --- a/frontend/src/ts/elements/alerts.ts +++ b/frontend/src/ts/elements/alerts.ts @@ -10,8 +10,9 @@ import * as ConnectionState from "../states/connection"; import { escapeHTML } from "../utils/misc"; import AnimatedModal from "../utils/animated-modal"; import { updateXp as accountPageUpdateProfile } from "./profile"; +import { MonkeyMail } from "@monkeytype/contracts/schemas/users"; -let accountAlerts: MonkeyTypes.MonkeyMail[] = []; +let accountAlerts: MonkeyMail[] = []; let maxMail = 0; let mailToMarkRead: string[] = []; let mailToDelete: string[] = []; diff --git a/frontend/src/ts/elements/keymap.ts b/frontend/src/ts/elements/keymap.ts index c1259efca..e40bb536c 100644 --- a/frontend/src/ts/elements/keymap.ts +++ b/frontend/src/ts/elements/keymap.ts @@ -10,7 +10,7 @@ import * as Notifications from "../elements/notifications"; import * as ActivePage from "../states/active-page"; import * as TestWords from "../test/test-words"; -const stenoKeys: MonkeyTypes.Layout = { +const stenoKeys: JSONData.Layout = { keymapShowTopRow: true, type: "matrix", keys: { @@ -171,7 +171,7 @@ export async function refresh( const rowIds = Object.keys(lts.keys); for (let index = 0; index < rowIds.length; index++) { - const row = rowIds[index] as keyof MonkeyTypes.Keys; + const row = rowIds[index] as keyof JSONData.Keys; let rowKeys = lts.keys[row]; if (row === "row1" && (isMatrix || Config.keymapStyle === "staggered")) { rowKeys = rowKeys.slice(1); diff --git a/frontend/src/ts/elements/notifications.ts b/frontend/src/ts/elements/notifications.ts index e8e876d8b..66e221198 100644 --- a/frontend/src/ts/elements/notifications.ts +++ b/frontend/src/ts/elements/notifications.ts @@ -266,10 +266,19 @@ function updateClearAllButton(): void { } } +export type AddNotificationOptions = { + important?: boolean; + duration?: number; + customTitle?: string; + customIcon?: string; + closeCallback?: () => void; + allowHTML?: boolean; +}; + export function add( message: string, level = 0, - options: MonkeyTypes.AddNotificationOptions = {} + options: AddNotificationOptions = {} ): void { NotificationEvent.dispatch(message, level, options.customTitle); diff --git a/frontend/src/ts/elements/profile.ts b/frontend/src/ts/elements/profile.ts index 199fe0372..7cb650581 100644 --- a/frontend/src/ts/elements/profile.ts +++ b/frontend/src/ts/elements/profile.ts @@ -16,7 +16,7 @@ import { abbreviateNumber, convertRemToPixels } from "../utils/numbers"; import { secondsToString } from "../utils/date-and-time"; type ProfileViewPaths = "profile" | "account"; -type UserProfileOrSnapshot = UserProfile | MonkeyTypes.Snapshot; +type UserProfileOrSnapshot = UserProfile | DB.Snapshot; //this is probably the dirtiest code ive ever written @@ -129,7 +129,7 @@ export async function update( const results = DB.getSnapshot()?.results; const lastResult = results?.[0]; - const streakOffset = (profile as MonkeyTypes.Snapshot).streakHourOffset; + const streakOffset = (profile as DB.Snapshot).streakHourOffset; const dayInMilis = 1000 * 60 * 60 * 24; diff --git a/frontend/src/ts/elements/test-activity-calendar.ts b/frontend/src/ts/elements/test-activity-calendar.ts index a2209ddf0..6eb9886f4 100644 --- a/frontend/src/ts/elements/test-activity-calendar.ts +++ b/frontend/src/ts/elements/test-activity-calendar.ts @@ -20,7 +20,17 @@ import { Interval, } from "date-fns"; -export class TestActivityCalendar implements MonkeyTypes.TestActivityCalendar { +type TestActivityDay = { + level: string; + label?: string; +}; + +export type TestActivityMonth = { + text: string; + weeks: number; +}; + +export class TestActivityCalendar implements TestActivityCalendar { protected data: (number | null | undefined)[]; protected startDay: Date; protected endDay: Date; @@ -69,12 +79,12 @@ export class TestActivityCalendar implements MonkeyTypes.TestActivityCalendar { return values.slice(offset); } - getMonths(): MonkeyTypes.TestActivityMonth[] { + getMonths(): TestActivityMonth[] { const months: Date[] = eachMonthOfInterval({ start: this.startDay, end: this.endDay, }); - const results: MonkeyTypes.TestActivityMonth[] = []; + const results: TestActivityMonth[] = []; for (let i = 0; i < months.length; i++) { const month: Date = months[i] as Date; @@ -101,8 +111,8 @@ export class TestActivityCalendar implements MonkeyTypes.TestActivityCalendar { return results; } - getDays(): MonkeyTypes.TestActivityDay[] { - const result: MonkeyTypes.TestActivityDay[] = []; + getDays(): TestActivityDay[] { + const result: TestActivityDay[] = []; const buckets = this.getBuckets(); const getValue = (v: number | null | undefined): string => { if (v === undefined) return "0"; @@ -172,7 +182,7 @@ export class TestActivityCalendar implements MonkeyTypes.TestActivityCalendar { export class ModifiableTestActivityCalendar extends TestActivityCalendar - implements MonkeyTypes.ModifiableTestActivityCalendar + implements ModifiableTestActivityCalendar { private lastDay: Date; @@ -205,7 +215,7 @@ export class ModifiableTestActivityCalendar this.data = this.buildData(this.data, this.lastDay); } - getFullYearCalendar(): MonkeyTypes.TestActivityCalendar { + getFullYearCalendar(): TestActivityCalendar { const today = new Date(); if (this.lastDay.getFullYear() !== new UTCDateMini(today).getFullYear()) { return new TestActivityCalendar([], today, true); diff --git a/frontend/src/ts/elements/test-activity.ts b/frontend/src/ts/elements/test-activity.ts index 51e328c7f..5dfa46418 100644 --- a/frontend/src/ts/elements/test-activity.ts +++ b/frontend/src/ts/elements/test-activity.ts @@ -3,11 +3,15 @@ import { DataObjectPartial } from "slim-select/store"; import { getTestActivityCalendar } from "../db"; import * as ServerConfiguration from "../ape/server-configuration"; import * as DB from "../db"; +import { + TestActivityCalendar, + TestActivityMonth, +} from "./test-activity-calendar"; let yearSelector: SlimSelect | undefined = undefined; export function init( - calendar?: MonkeyTypes.TestActivityCalendar, + calendar?: TestActivityCalendar, userSignUpDate?: Date ): void { if (calendar === undefined) { @@ -21,7 +25,7 @@ export function init( update(calendar); } -function update(calendar?: MonkeyTypes.TestActivityCalendar): void { +function update(calendar?: TestActivityCalendar): void { const container = document.querySelector("#testActivity .activity"); if (container === null) { @@ -89,7 +93,7 @@ export function initYearSelector( years.length > 1 ? yearSelect.enable() : yearSelect.disable(); } -function updateMonths(months: MonkeyTypes.TestActivityMonth[]): void { +function updateMonths(months: TestActivityMonth[]): void { const element = document.querySelector("#testActivity .months") as Element; element.innerHTML = months diff --git a/frontend/src/ts/modals/edit-preset.ts b/frontend/src/ts/modals/edit-preset.ts index b0124e80a..a2f1d9bb3 100644 --- a/frontend/src/ts/modals/edit-preset.ts +++ b/frontend/src/ts/modals/edit-preset.ts @@ -16,6 +16,7 @@ import { } from "@monkeytype/contracts/schemas/presets"; import { getPreset } from "../controllers/preset-controller"; import defaultConfig from "../constants/default-config"; +import { Config as ConfigType } from "@monkeytype/contracts/schemas/configs"; const state = { presetType: "full" as PresetType, @@ -279,12 +280,12 @@ async function apply(): Promise<void> { }), display: propPresetName, _id: response.body.data.presetId, - } as MonkeyTypes.SnapshotPreset); + } as DB.SnapshotPreset); } } else if (action === "edit") { const preset = snapshotPresets.filter( - (preset: MonkeyTypes.SnapshotPreset) => preset._id === presetId - )[0] as MonkeyTypes.SnapshotPreset; + (preset: DB.SnapshotPreset) => preset._id === presetId + )[0] as DB.SnapshotPreset; if (preset === undefined) { Notifications.add("Preset not found", -1); return; @@ -329,13 +330,11 @@ async function apply(): Promise<void> { ); } else { Notifications.add("Preset removed", 1); - snapshotPresets.forEach( - (preset: MonkeyTypes.SnapshotPreset, index: number) => { - if (preset._id === presetId) { - snapshotPresets.splice(index, 1); - } + snapshotPresets.forEach((preset: DB.SnapshotPreset, index: number) => { + if (preset._id === presetId) { + snapshotPresets.splice(index, 1); } - ); + }); } } @@ -467,16 +466,16 @@ function getSettingGroup(configFieldName: string): PresetSettingGroup { } function getPartialConfigChanges( - configChanges: MonkeyTypes.ConfigChanges -): MonkeyTypes.ConfigChanges { - const activeConfigChanges: MonkeyTypes.ConfigChanges = {}; + configChanges: Partial<ConfigType> +): Partial<ConfigType> { + const activeConfigChanges: Partial<ConfigType> = {}; Object.keys(defaultConfig) .filter( (settingName) => state.checkboxes.get(getSettingGroup(settingName)) === true ) .forEach((settingName) => { - const safeSettingName = settingName as keyof MonkeyTypes.ConfigChanges; + const safeSettingName = settingName as keyof Partial<ConfigType>; const newValue = configChanges[safeSettingName] !== undefined ? configChanges[safeSettingName] @@ -493,7 +492,7 @@ function getActiveSettingGroupsFromState(): ActiveSettingGroups { .map(([key]) => key) ); } -function getConfigChanges(): MonkeyTypes.ConfigChanges { +function getConfigChanges(): Partial<ConfigType> { const activeConfigChanges = state.presetType === "partial" ? getPartialConfigChanges(Config.getConfigChanges()) @@ -501,9 +500,8 @@ function getConfigChanges(): MonkeyTypes.ConfigChanges { const tags = DB.getSnapshot()?.tags ?? []; const activeTagIds: string[] = tags - .filter((tag: MonkeyTypes.UserTag) => tag.active) - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - .map((tag: MonkeyTypes.UserTag) => tag._id); + .filter((tag) => tag.active) + .map((tag) => tag._id); const setTags: boolean = state.presetType === "full" || state.checkboxes.get("behavior") === true; diff --git a/frontend/src/ts/modals/edit-result-tags.ts b/frontend/src/ts/modals/edit-result-tags.ts index 332e5eb9f..f6d2ee932 100644 --- a/frontend/src/ts/modals/edit-result-tags.ts +++ b/frontend/src/ts/modals/edit-result-tags.ts @@ -7,8 +7,6 @@ import * as ConnectionState from "../states/connection"; import { areUnsortedArraysEqual } from "../utils/arrays"; import * as TestResult from "../test/result"; import AnimatedModal from "../utils/animated-modal"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; -import { Result } from "@monkeytype/contracts/schemas/results"; type State = { resultId: string; @@ -137,7 +135,7 @@ async function save(): Promise<void> { duration: 2, }); - DB.getSnapshot()?.results?.forEach((result: Result<Mode>) => { + DB.getSnapshot()?.results?.forEach((result) => { if (result._id === state.resultId) { result.tags = state.tags; } diff --git a/frontend/src/ts/modals/quote-rate.ts b/frontend/src/ts/modals/quote-rate.ts index 07193efb9..918946644 100644 --- a/frontend/src/ts/modals/quote-rate.ts +++ b/frontend/src/ts/modals/quote-rate.ts @@ -1,4 +1,5 @@ import Ape from "../ape"; +import { Quote } from "../controllers/quotes-controller"; import * as DB from "../db"; import * as Loader from "../elements/loader"; import * as Notifications from "../elements/notifications"; @@ -15,7 +16,7 @@ type QuoteStats = { }; let quoteStats: QuoteStats | null | Record<string, never> = null; -let currentQuote: MonkeyTypes.Quote | null = null; +let currentQuote: Quote | null = null; export function clearQuoteStats(): void { quoteStats = null; @@ -39,7 +40,7 @@ function getRatingAverage(quoteStats: QuoteStats): number { } export async function getQuoteStats( - quote?: MonkeyTypes.Quote + quote?: Quote ): Promise<QuoteStats | undefined> { if (!quote) { return; @@ -106,10 +107,7 @@ function updateData(): void { void updateRatingStats(); } -export function show( - quote: MonkeyTypes.Quote, - showOptions?: ShowOptions -): void { +export function show(quote: Quote, showOptions?: ShowOptions): void { void modal.show({ ...showOptions, beforeAnimation: async () => { diff --git a/frontend/src/ts/modals/quote-report.ts b/frontend/src/ts/modals/quote-report.ts index fc3a5f573..425ae56b5 100644 --- a/frontend/src/ts/modals/quote-report.ts +++ b/frontend/src/ts/modals/quote-report.ts @@ -2,7 +2,7 @@ import Ape from "../ape"; import Config from "../config"; import * as Loader from "../elements/loader"; import * as Notifications from "../elements/notifications"; -import QuotesController from "../controllers/quotes-controller"; +import QuotesController, { Quote } from "../controllers/quotes-controller"; import * as CaptchaController from "../controllers/captcha-controller"; import { removeLanguageSize } from "../utils/strings"; import SlimSelect from "slim-select"; @@ -11,7 +11,7 @@ import { CharacterCounter } from "../elements/character-counter"; import { QuoteReportReason } from "@monkeytype/contracts/schemas/quotes"; type State = { - quoteToReport?: MonkeyTypes.Quote; + quoteToReport?: Quote; reasonSelect?: SlimSelect | undefined; }; diff --git a/frontend/src/ts/modals/quote-search.ts b/frontend/src/ts/modals/quote-search.ts index 9192853d8..ffc0be97b 100644 --- a/frontend/src/ts/modals/quote-search.ts +++ b/frontend/src/ts/modals/quote-search.ts @@ -11,7 +11,7 @@ import { TextExtractor, } from "../utils/search-service"; import { splitByAndKeep } from "../utils/strings"; -import QuotesController from "../controllers/quotes-controller"; +import QuotesController, { Quote } from "../controllers/quotes-controller"; import { isAuthenticated } from "../firebase"; import { debounce } from "throttle-debounce"; import Ape from "../ape"; @@ -23,7 +23,7 @@ import * as TestLogic from "../test/test-logic"; import { createErrorMessage } from "../utils/misc"; import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; -const searchServiceCache: Record<string, SearchService<MonkeyTypes.Quote>> = {}; +const searchServiceCache: Record<string, SearchService<Quote>> = {}; const pageSize = 100; let currentPageNumber = 1; @@ -61,9 +61,7 @@ function highlightMatches(text: string, matchedText: string[]): string { return normalizedWords.join(""); } -function applyQuoteLengthFilter( - quotes: MonkeyTypes.Quote[] -): MonkeyTypes.Quote[] { +function applyQuoteLengthFilter(quotes: Quote[]): Quote[] { const quoteLengthFilterValue = $( "#quoteSearchModal .quoteLengthFilter" ).val() as string[]; @@ -81,7 +79,7 @@ function applyQuoteLengthFilter( return filteredQuotes; } -function applyQuoteFavFilter(quotes: MonkeyTypes.Quote[]): MonkeyTypes.Quote[] { +function applyQuoteFavFilter(quotes: Quote[]): Quote[] { const showFavOnly = ( document.querySelector(".toggleFavorites") as HTMLDivElement ).classList.contains("active"); @@ -98,7 +96,7 @@ function applyQuoteFavFilter(quotes: MonkeyTypes.Quote[]): MonkeyTypes.Quote[] { } function buildQuoteSearchResult( - quote: MonkeyTypes.Quote, + quote: Quote, matchedSearchTerms: string[] ): string { let lengthDesc; @@ -158,10 +156,10 @@ function buildQuoteSearchResult( async function updateResults(searchText: string): Promise<void> { const { quotes } = await QuotesController.getQuotes(Config.language); - const quoteSearchService = getSearchService<MonkeyTypes.Quote>( + const quoteSearchService = getSearchService<Quote>( Config.language, quotes, - (quote: MonkeyTypes.Quote) => { + (quote: Quote) => { return `${quote.text} ${quote.id} ${quote.source}`; } ); @@ -357,7 +355,7 @@ async function toggleFavoriteForQuote(quoteId: string): Promise<void> { const quote = { language: quoteLang, id: parseInt(quoteId, 10), - } as MonkeyTypes.Quote; + } as Quote; const alreadyFavorited = QuotesController.isQuoteFavorite(quote); diff --git a/frontend/src/ts/modals/share-test-settings.ts b/frontend/src/ts/modals/share-test-settings.ts index 8a0cf4ba5..54ba7d185 100644 --- a/frontend/src/ts/modals/share-test-settings.ts +++ b/frontend/src/ts/modals/share-test-settings.ts @@ -16,7 +16,7 @@ function getCheckboxValue(checkbox: string): boolean { type SharedTestSettings = [ Mode | null, Mode2<Mode> | null, - MonkeyTypes.CustomTextData | null, + CustomText.CustomTextData | null, boolean | null, boolean | null, string | null, diff --git a/frontend/src/ts/modals/streak-hour-offset.ts b/frontend/src/ts/modals/streak-hour-offset.ts index 1b6e0089e..0c11bf068 100644 --- a/frontend/src/ts/modals/streak-hour-offset.ts +++ b/frontend/src/ts/modals/streak-hour-offset.ts @@ -4,7 +4,7 @@ import * as Notifications from "../elements/notifications"; import * as Loader from "../elements/loader"; // import * as Settings from "../pages/settings"; import * as ConnectionState from "../states/connection"; -import { getSnapshot, setSnapshot } from "../db"; +import { getSnapshot, setSnapshot, Snapshot } from "../db"; import AnimatedModal from "../utils/animated-modal"; export function show(): void { @@ -93,7 +93,7 @@ async function apply(): Promise<void> { ); } else { Notifications.add("Streak hour offset set", 1); - const snap = getSnapshot() as MonkeyTypes.Snapshot; + const snap = getSnapshot() as Snapshot; snap.streakHourOffset = value; setSnapshot(snap); hide(); diff --git a/frontend/src/ts/modals/version-history.ts b/frontend/src/ts/modals/version-history.ts index 3df9b29fc..b57c738a0 100644 --- a/frontend/src/ts/modals/version-history.ts +++ b/frontend/src/ts/modals/version-history.ts @@ -14,7 +14,7 @@ export function show(): void { getReleasesFromGitHub() .then((releases) => { $("#versionHistoryModal .modal").html(`<div class="releases"></div`); - releases.forEach((release: MonkeyTypes.GithubRelease) => { + releases.forEach((release) => { if (!release.draft && !release.prerelease) { let body = release.body; diff --git a/frontend/src/ts/modals/word-filter.ts b/frontend/src/ts/modals/word-filter.ts index 1ff21624e..6bc20fe2c 100644 --- a/frontend/src/ts/modals/word-filter.ts +++ b/frontend/src/ts/modals/word-filter.ts @@ -10,8 +10,8 @@ import AnimatedModal, { type FilterPreset = { display: string; - getIncludeString: (layout: MonkeyTypes.Layout) => string[]; - getExcludeString: (layout: MonkeyTypes.Layout) => string[]; + getIncludeString: (layout: JSONData.Layout) => string[]; + getExcludeString: (layout: JSONData.Layout) => string[]; }; const presets: Record<string, FilterPreset> = { diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index d4470095d..de8c5954d 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -28,9 +28,15 @@ import * as ResultBatches from "../elements/result-batches"; import Format from "../utils/format"; import * as TestActivity from "../elements/test-activity"; import { ChartData } from "@monkeytype/contracts/schemas/results"; -import { Mode, Mode2, Mode2Custom } from "@monkeytype/contracts/schemas/shared"; +import { + Difficulty, + Mode, + Mode2, + Mode2Custom, +} from "@monkeytype/contracts/schemas/shared"; import { ResultFiltersGroupItem } from "@monkeytype/contracts/schemas/users"; import { findLineByLeastSquares } from "../utils/numbers"; +import defaultResultFilters from "../constants/default-result-filters"; let filterDebug = false; //toggle filterdebug @@ -41,7 +47,7 @@ export function toggleFilterDebug(): void { } } -let filteredResults: MonkeyTypes.FullResult<Mode>[] = []; +let filteredResults: DB.SnapshotResult<Mode>[] = []; let visibleTableLines = 0; function loadMoreLines(lineIndex?: number): void { @@ -202,8 +208,8 @@ function reset(): void { } let totalSecondsFiltered = 0; -let chartData: MonkeyTypes.HistoryChartData[] = []; -let accChartData: MonkeyTypes.AccChartData[] = []; +let chartData: ChartController.HistoryChartData[] = []; +let accChartData: ChartController.AccChartData[] = []; async function fillContent(): Promise<void> { LoadingPage.updateText("Displaying stats..."); @@ -288,7 +294,7 @@ async function fillContent(): Promise<void> { if (resdiff === undefined) { resdiff = "normal"; } - if (!ResultFilters.getFilter("difficulty", resdiff)) { + if (!ResultFilters.getFilter("difficulty", resdiff as Difficulty)) { if (filterDebug) { console.log(`skipping result due to difficulty filter`, result); } @@ -344,7 +350,8 @@ async function fillContent(): Promise<void> { } if (result.quoteLength !== null) { - let filter: MonkeyTypes.QuoteModes | undefined = undefined; + let filter: keyof typeof defaultResultFilters.quoteLength | undefined = + undefined; if (result.quoteLength === 0) { filter = "short"; } else if (result.quoteLength === 1) { @@ -519,7 +526,7 @@ async function fillContent(): Promise<void> { dataForTimestamp.amount++; dataForTimestamp.time += result.testDuration + - result.incompleteTestSeconds - + (result.incompleteTestSeconds ?? 0) - (result.afkDuration ?? 0); dataForTimestamp.totalWpm += result.wpm; } else { @@ -527,7 +534,7 @@ async function fillContent(): Promise<void> { amount: 1, time: result.testDuration + - result.incompleteTestSeconds - + (result.incompleteTestSeconds ?? 0) - (result.afkDuration ?? 0), totalWpm: result.wpm, }; @@ -660,9 +667,9 @@ async function fillContent(): Promise<void> { loadMoreLines(); //////// - const activityChartData_timeAndAmount: MonkeyTypes.ActivityChartDataPoint[] = + const activityChartData_timeAndAmount: ChartController.ActivityChartDataPoint[] = []; - const activityChartData_avgWpm: MonkeyTypes.ActivityChartDataPoint[] = []; + const activityChartData_avgWpm: ChartController.ActivityChartDataPoint[] = []; const wpmStepSize = typingSpeedUnit.historyStepSize; // let lastTimestamp = 0; @@ -736,7 +743,7 @@ async function fillContent(): Promise<void> { const pb: { x: number; y: number }[] = []; for (let i = chartData.length - 1; i >= 0; i--) { - const a = chartData[i] as MonkeyTypes.HistoryChartData; + const a = chartData[i] as ChartController.HistoryChartData; if (a.y > currentPb) { currentPb = a.y; pb.push(a); @@ -1065,7 +1072,7 @@ function sortAndRefreshHistory( $(headerClass).append('<i class="fas fa-sort-up", aria-hidden="true"></i>'); } - const temp: MonkeyTypes.FullResult<Mode>[] = []; + const temp: DB.SnapshotResult<Mode>[] = []; const parsedIndexes: number[] = []; while (temp.length < filteredResults.length) { @@ -1224,6 +1231,7 @@ $(".pageAccount .group.presetFilterButtons").on( ); $(".pageAccount .content .group.aboveHistory .exportCSV").on("click", () => { + //@ts-expect-error void Misc.downloadResultsCSV(filteredResults); }); diff --git a/frontend/src/ts/pages/page.ts b/frontend/src/ts/pages/page.ts index f08595d14..397f39088 100644 --- a/frontend/src/ts/pages/page.ts +++ b/frontend/src/ts/pages/page.ts @@ -1,10 +1,22 @@ +export type PageName = + | "loading" + | "test" + | "settings" + | "about" + | "account" + | "login" + | "profile" + | "profileSearch" + | "404" + | "accountSettings"; + type Options<T> = { params?: Record<string, string>; data?: T; }; type PageProperties<T> = { - name: MonkeyTypes.PageName; + name: PageName; element: JQuery; path: string; beforeHide?: () => Promise<void>; @@ -17,7 +29,7 @@ async function empty(): Promise<void> { return; } export default class Page<T> { - public name: MonkeyTypes.PageName; + public name: PageName; public element: JQuery; public pathname: string; public beforeHide: () => Promise<void>; diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 8a6325dcd..3fe334dae 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -21,7 +21,10 @@ import SlimSelect from "slim-select"; import * as Skeleton from "../utils/skeleton"; import * as CustomBackgroundFilter from "../elements/custom-background-filter"; -import { ConfigValue } from "@monkeytype/contracts/schemas/configs"; +import { + ConfigValue, + CustomLayoutFluid, +} from "@monkeytype/contracts/schemas/configs"; type SettingsGroups<T extends ConfigValue> = Record<string, SettingsGroup<T>>; @@ -787,7 +790,7 @@ function refreshTagsSettingsSection(): void { function refreshPresetsSettingsSection(): void { if (isAuthenticated() && DB.getSnapshot()) { const presetsEl = $(".pageSettings .section.presets .presetsList").empty(); - DB.getSnapshot()?.presets?.forEach((preset: MonkeyTypes.SnapshotPreset) => { + DB.getSnapshot()?.presets?.forEach((preset: DB.SnapshotPreset) => { presetsEl.append(` <div class="buttons preset" data-id="${preset._id}" data-name="${preset.name}" data-display="${preset.display}"> <button class="presetButton">${preset.display}</button> @@ -1274,7 +1277,7 @@ $( void UpdateConfig.setCustomLayoutfluid( $( ".pageSettings .section[data-config-name='customLayoutfluid'] .inputAndButton input" - ).val() as MonkeyTypes.CustomLayoutFluidSpaces + ).val() as CustomLayoutFluid ).then((bool) => { if (bool) { Notifications.add("Custom layoutfluid saved", 1); @@ -1289,7 +1292,7 @@ $( void UpdateConfig.setCustomLayoutfluid( $( ".pageSettings .section[data-config-name='customLayoutfluid'] .inputAndButton input" - ).val() as MonkeyTypes.CustomLayoutFluidSpaces + ).val() as CustomLayoutFluid ).then((bool) => { if (bool) { Notifications.add("Custom layoutfluid saved", 1); diff --git a/frontend/src/ts/states/active-page.ts b/frontend/src/ts/states/active-page.ts index 5977afbcb..fa8f96f8f 100644 --- a/frontend/src/ts/states/active-page.ts +++ b/frontend/src/ts/states/active-page.ts @@ -1,9 +1,11 @@ -let activePage: MonkeyTypes.PageName = "loading"; +import { PageName } from "../pages/page"; -export function get(): MonkeyTypes.PageName { +let activePage: PageName = "loading"; + +export function get(): PageName { return activePage; } -export function set(active: MonkeyTypes.PageName): void { +export function set(active: PageName): void { activePage = active; } diff --git a/frontend/src/ts/test/custom-text.ts b/frontend/src/ts/test/custom-text.ts index 3614196fd..a0d59cb2f 100644 --- a/frontend/src/ts/test/custom-text.ts +++ b/frontend/src/ts/test/custom-text.ts @@ -6,6 +6,7 @@ import { } from "@monkeytype/contracts/schemas/util"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; import { z } from "zod"; +import { CustomTextDataWithTextLen } from "@monkeytype/contracts/schemas/results"; const CustomTextObjectSchema = z.record(z.string(), z.string()); type CustomTextObject = z.infer<typeof CustomTextObjectSchema>; @@ -37,6 +38,8 @@ const CustomTextSettingsSchema = z.object({ type CustomTextSettings = z.infer<typeof CustomTextSettingsSchema>; +type CustomTextLimit = z.infer<typeof CustomTextSettingsSchema>["limit"]; + const defaultCustomTextSettings: CustomTextSettings = { text: ["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"], mode: "repeat", @@ -93,8 +96,8 @@ export function setMode(val: CustomTextMode): void { }); } -export function getLimit(): MonkeyTypes.CustomTextLimit { - return customTextSettings.get().limit as MonkeyTypes.CustomTextLimit; +export function getLimit(): CustomTextLimit { + return customTextSettings.get().limit as CustomTextLimit; } export function getLimitValue(): number { @@ -133,7 +136,11 @@ export function setPipeDelimiter(val: boolean): void { }); } -export function getData(): MonkeyTypes.CustomTextData { +export type CustomTextData = Omit<CustomTextDataWithTextLen, "textLen"> & { + text: string[]; +}; + +export function getData(): CustomTextData { return { text: getText(), mode: getMode(), diff --git a/frontend/src/ts/test/funbox/funbox-list.ts b/frontend/src/ts/test/funbox/funbox-list.ts index ede52df88..9090c7a74 100644 --- a/frontend/src/ts/test/funbox/funbox-list.ts +++ b/frontend/src/ts/test/funbox/funbox-list.ts @@ -1,4 +1,6 @@ -const list: MonkeyTypes.FunboxMetadata[] = [ +import { FunboxFunctions, FunboxMetadata } from "../../utils/json-data"; + +const list: FunboxMetadata[] = [ { name: "nausea", info: "I think I'm gonna be sick.", @@ -280,12 +282,12 @@ const list: MonkeyTypes.FunboxMetadata[] = [ }, ]; -export function getAll(): MonkeyTypes.FunboxMetadata[] { +export function getAll(): FunboxMetadata[] { return list; } -export function get(config: string): MonkeyTypes.FunboxMetadata[] { - const funboxes: MonkeyTypes.FunboxMetadata[] = []; +export function get(config: string): FunboxMetadata[] { + const funboxes: FunboxMetadata[] = []; for (const i of config.split("#")) { const f = list.find((f) => f.name === i); if (f) funboxes.push(f); @@ -293,10 +295,7 @@ export function get(config: string): MonkeyTypes.FunboxMetadata[] { return funboxes; } -export function setFunboxFunctions( - name: string, - obj: MonkeyTypes.FunboxFunctions -): void { +export function setFunboxFunctions(name: string, obj: FunboxFunctions): void { const fb = list.find((f) => f.name === name); if (!fb) throw new Error(`Funbox ${name} not found.`); fb.functions = obj; diff --git a/frontend/src/ts/test/funbox/funbox-validation.ts b/frontend/src/ts/test/funbox/funbox-validation.ts index 9a9f37033..31b132b9d 100644 --- a/frontend/src/ts/test/funbox/funbox-validation.ts +++ b/frontend/src/ts/test/funbox/funbox-validation.ts @@ -3,6 +3,7 @@ import * as Notifications from "../../elements/notifications"; import * as Strings from "../../utils/strings"; import { Config, ConfigValue } from "@monkeytype/contracts/schemas/configs"; import { intersect } from "@monkeytype/util/arrays"; +import { FunboxForcedConfig, FunboxMetadata } from "../../utils/json-data"; export function checkFunboxForcedConfigs( key: string, @@ -79,7 +80,7 @@ export function canSetConfigWithCurrentFunboxes( ): boolean { let errorCount = 0; if (key === "mode") { - let fb: MonkeyTypes.FunboxMetadata[] = []; + let fb: FunboxMetadata[] = []; fb = fb.concat( FunboxList.get(funbox).filter( (f) => @@ -295,7 +296,7 @@ export function areFunboxesCompatible( funboxesToCheck.filter((f) => f.functions?.isCharCorrect).length <= 1; const oneCharReplacerMax = funboxesToCheck.filter((f) => f.functions?.getWordHtml).length <= 1; - const allowedConfig = {} as MonkeyTypes.FunboxForcedConfig; + const allowedConfig = {} as FunboxForcedConfig; let noConfigConflicts = true; for (const f of funboxesToCheck) { if (!f.forcedConfig) continue; diff --git a/frontend/src/ts/test/funbox/funbox.ts b/frontend/src/ts/test/funbox/funbox.ts index b020cb2df..fff8f111b 100644 --- a/frontend/src/ts/test/funbox/funbox.ts +++ b/frontend/src/ts/test/funbox/funbox.ts @@ -22,7 +22,7 @@ import { areFunboxesCompatible, checkFunboxForcedConfigs, } from "./funbox-validation"; -import { Wordset } from "../wordset"; +import { FunboxWordsFrequency, Wordset } from "../wordset"; import * as LayoutfluidFunboxTimer from "./layoutfluid-funbox-timer"; import * as DDR from "../../utils/ddr"; import { HighlightMode } from "@monkeytype/contracts/schemas/configs"; @@ -432,13 +432,13 @@ FunboxList.setFunboxFunctions("nospace", { }); FunboxList.setFunboxFunctions("poetry", { - async pullSection(): Promise<Misc.Section | false> { + async pullSection(): Promise<JSONData.Section | false> { return getPoem(); }, }); FunboxList.setFunboxFunctions("wikipedia", { - async pullSection(lang?: string): Promise<Misc.Section | false> { + async pullSection(lang?: string): Promise<JSONData.Section | false> { return getSection((lang ?? "") || "english"); }, }); @@ -512,7 +512,7 @@ FunboxList.setFunboxFunctions("hexadecimal", { }); FunboxList.setFunboxFunctions("zipf", { - getWordsFrequencyMode(): MonkeyTypes.FunboxWordsFrequency { + getWordsFrequencyMode(): FunboxWordsFrequency { return "zipf"; }, }); diff --git a/frontend/src/ts/test/lazy-mode.ts b/frontend/src/ts/test/lazy-mode.ts index 3f8a73b37..153dac2c4 100644 --- a/frontend/src/ts/test/lazy-mode.ts +++ b/frontend/src/ts/test/lazy-mode.ts @@ -1,4 +1,4 @@ -const accents: [string, string][] = [ +const accents: Accents = [ ["áàâäåãąą́āą̄ă", "a"], ["éèêëẽęę́ēę̄ėě", "e"], ["íìîïĩįį́īį̄ı", "i"], @@ -46,9 +46,11 @@ const accentsMap = new Map<string, string>( accents.flatMap((rule) => [...rule[0]].map((accent) => [accent, rule[1]])) ); +export type Accents = [string, string][]; + function findAccent( char: string, - additionalAccents?: MonkeyTypes.Accents + additionalAccents?: Accents ): string | undefined { const lookup = char.toLowerCase(); @@ -59,7 +61,7 @@ function findAccent( export function replaceAccents( word: string, - additionalAccents?: MonkeyTypes.Accents + additionalAccents?: Accents ): string { if (!word) return word; const uppercased = word.toUpperCase(); diff --git a/frontend/src/ts/test/poetry.ts b/frontend/src/ts/test/poetry.ts index ab104ac10..c127a1602 100644 --- a/frontend/src/ts/test/poetry.ts +++ b/frontend/src/ts/test/poetry.ts @@ -1,4 +1,4 @@ -import { Section } from "../utils/misc"; +import { Section } from "../utils/json-data"; const bannedChars = ["—", "_", " "]; const maxWords = 100; diff --git a/frontend/src/ts/test/practise-words.ts b/frontend/src/ts/test/practise-words.ts index 4a9e08634..7e7cbb0e4 100644 --- a/frontend/src/ts/test/practise-words.ts +++ b/frontend/src/ts/test/practise-words.ts @@ -11,7 +11,7 @@ type Before = { mode: Mode | null; punctuation: boolean | null; numbers: boolean | null; - customText: MonkeyTypes.CustomTextData | null; + customText: CustomText.CustomTextData | null; }; export const before: Before = { diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 5f8feabc7..5166315f9 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -3,7 +3,7 @@ import { Chart, type PluginChartOptions } from "chart.js"; import Config from "../config"; import * as AdController from "../controllers/ad-controller"; import * as ChartController from "../controllers/chart-controller"; -import QuotesController from "../controllers/quotes-controller"; +import QuotesController, { Quote } from "../controllers/quotes-controller"; import * as DB from "../db"; import * as Loader from "../elements/loader"; import * as Notifications from "../elements/notifications"; @@ -538,7 +538,7 @@ export function showConfetti(): void { } async function updateTags(dontSave: boolean): Promise<void> { - const activeTags: MonkeyTypes.UserTag[] = []; + const activeTags: DB.SnapshotUserTag[] = []; const userTagsCount = DB.getSnapshot()?.tags?.length ?? 0; try { DB.getSnapshot()?.tags?.forEach((tag) => { @@ -654,7 +654,7 @@ async function updateTags(dontSave: boolean): Promise<void> { }); } -function updateTestType(randomQuote: MonkeyTypes.Quote | null): void { +function updateTestType(randomQuote: Quote | null): void { let testType = ""; testType += Config.mode; @@ -761,7 +761,7 @@ function updateOther( } } -export function updateRateQuote(randomQuote: MonkeyTypes.Quote | null): void { +export function updateRateQuote(randomQuote: Quote | null): void { if (Config.mode === "quote") { if (randomQuote === null) { console.error( @@ -794,7 +794,7 @@ export function updateRateQuote(randomQuote: MonkeyTypes.Quote | null): void { } } -function updateQuoteFavorite(randomQuote: MonkeyTypes.Quote | null): void { +function updateQuoteFavorite(randomQuote: Quote | null): void { const icon = $(".pageTest #result #favoriteQuoteButton .icon"); if (Config.mode !== "quote" || !isAuthenticated()) { @@ -817,7 +817,7 @@ function updateQuoteFavorite(randomQuote: MonkeyTypes.Quote | null): void { icon.parent().removeClass("hidden"); } -function updateQuoteSource(randomQuote: MonkeyTypes.Quote | null): void { +function updateQuoteSource(randomQuote: Quote | null): void { if (Config.mode === "quote") { $("#result .stats .source").removeClass("hidden"); $("#result .stats .source .bottom").html( @@ -835,7 +835,7 @@ export async function update( afkDetected: boolean, isRepeated: boolean, tooShort: boolean, - randomQuote: MonkeyTypes.Quote | null, + randomQuote: Quote | null, dontSave: boolean ): Promise<void> { resultAnnotation = []; diff --git a/frontend/src/ts/test/shift-tracker.ts b/frontend/src/ts/test/shift-tracker.ts index 627e5679e..802100a6a 100644 --- a/frontend/src/ts/test/shift-tracker.ts +++ b/frontend/src/ts/test/shift-tracker.ts @@ -283,7 +283,7 @@ export function isUsingOppositeShift(keycode: string): boolean { export function layoutKeyToKeycode( key: string, - layout: MonkeyTypes.Layout + layout: JSONData.Layout ): string | undefined { const rows: string[][] = Object.values(layout.keys); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 2a2932404..7bc8e5bac 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1181,9 +1181,12 @@ async function saveResult( } if (data.insertedId !== undefined) { - const result = JSON.parse( - JSON.stringify(completedEvent) - ) as MonkeyTypes.FullResult<Mode>; + //TODO - this type cast was not needed before because we were using JSON cloning + // but now with the stronger types it shows that we are forcing completed event + // into a snapshot result - might not cuase issues but worth investigating + const result = Misc.deepClone( + completedEvent + ) as unknown as DB.SnapshotResult<Mode>; result._id = data.insertedId; if (data.isPb !== undefined && data.isPb) { result.isPb = true; diff --git a/frontend/src/ts/test/test-state.ts b/frontend/src/ts/test/test-state.ts index e996aec00..85a23ec7a 100644 --- a/frontend/src/ts/test/test-state.ts +++ b/frontend/src/ts/test/test-state.ts @@ -1,7 +1,9 @@ +import { Challenge } from "../utils/json-data"; + export let isRepeated = false; export let isPaceRepeat = false; export let isActive = false; -export let activeChallenge: null | MonkeyTypes.Challenge = null; +export let activeChallenge: null | Challenge = null; export let savingEnabled = true; export let bailedOut = false; export let selectedQuoteId = 1; @@ -18,7 +20,7 @@ export function setActive(tf: boolean): void { isActive = tf; } -export function setActiveChallenge(val: null | MonkeyTypes.Challenge): void { +export function setActiveChallenge(val: null | Challenge): void { activeChallenge = val; } diff --git a/frontend/src/ts/test/test-stats.ts b/frontend/src/ts/test/test-stats.ts index 33b52db43..44fb8dcbd 100644 --- a/frontend/src/ts/test/test-stats.ts +++ b/frontend/src/ts/test/test-stats.ts @@ -142,9 +142,10 @@ export function calculateTestSeconds(now?: number): number { } } -export function calculateWpmAndRaw( - withDecimalPoints?: true -): MonkeyTypes.WpmAndRaw { +export function calculateWpmAndRaw(withDecimalPoints?: true): { + wpm: number; + raw: number; +} { const testSeconds = calculateTestSeconds( TestState.isActive ? performance.now() : end ); diff --git a/frontend/src/ts/test/test-timer.ts b/frontend/src/ts/test/test-timer.ts index 0303089bb..1cbf5e19f 100644 --- a/frontend/src/ts/test/test-timer.ts +++ b/frontend/src/ts/test/test-timer.ts @@ -18,6 +18,13 @@ import * as Time from "../states/time"; import * as TimerEvent from "../observables/timer-event"; import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer"; +type TimerStats = { + dateNow: number; + now: number; + expected: number; + nextDelay: number; +}; + let slowTimerCount = 0; let timer: NodeJS.Timeout | null = null; const interval = 1000; @@ -54,7 +61,7 @@ function updateTimer(): void { if (timerDebug) console.timeEnd("timer progress update"); } -function calculateWpmRaw(): MonkeyTypes.WpmAndRaw { +function calculateWpmRaw(): { wpm: number; raw: number } { if (timerDebug) console.time("calculate wpm and raw"); const wpmAndRaw = TestStats.calculateWpmAndRaw(); if (timerDebug) console.timeEnd("calculate wpm and raw"); @@ -68,7 +75,7 @@ function calculateWpmRaw(): MonkeyTypes.WpmAndRaw { return wpmAndRaw; } -function monkey(wpmAndRaw: MonkeyTypes.WpmAndRaw): void { +function monkey(wpmAndRaw: { wpm: number; raw: number }): void { if (timerDebug) console.time("update monkey"); const num = Config.blindMode ? wpmAndRaw.raw : wpmAndRaw.wpm; Monkey.updateFastOpacity(num); @@ -119,7 +126,10 @@ function layoutfluid(): void { if (timerDebug) console.timeEnd("layoutfluid"); } -function checkIfFailed(wpmAndRaw: MonkeyTypes.WpmAndRaw, acc: number): void { +function checkIfFailed( + wpmAndRaw: { wpm: number; raw: number }, + acc: number +): void { if (timerDebug) console.time("fail conditions"); TestInput.pushKeypressesToHistory(); TestInput.pushErrorToHistory(); @@ -175,9 +185,9 @@ function checkIfTimeIsUp(): void { // --------------------------------------- -let timerStats: MonkeyTypes.TimerStats[] = []; +let timerStats: TimerStats[] = []; -export function getTimerStats(): MonkeyTypes.TimerStats[] { +export function getTimerStats(): TimerStats[] { return timerStats; } diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 4385ae7df..5a0b825ff 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -113,7 +113,7 @@ async function joinOverlappingHints( } const debouncedZipfCheck = debounce(250, async () => { - const supports = await Misc.checkIfLanguageSupportsZipf(Config.language); + const supports = await JSONData.checkIfLanguageSupportsZipf(Config.language); if (supports === "no") { Notifications.add( `${Strings.capitalizeFirstLetter( diff --git a/frontend/src/ts/test/test-words.ts b/frontend/src/ts/test/test-words.ts index db0e79a37..d2ee8a778 100644 --- a/frontend/src/ts/test/test-words.ts +++ b/frontend/src/ts/test/test-words.ts @@ -1,3 +1,5 @@ +import { QuoteWithTextSplit } from "../controllers/quotes-controller"; + class Words { public list: string[]; public sectionIndexList: number[]; @@ -69,11 +71,9 @@ export const words = new Words(); export let hasTab = false; export let hasNewline = false; export let hasNumbers = false; -export let currentQuote = null as MonkeyTypes.QuoteWithTextSplit | null; +export let currentQuote = null as QuoteWithTextSplit | null; -export function setCurrentQuote( - rq: MonkeyTypes.QuoteWithTextSplit | null -): void { +export function setCurrentQuote(rq: QuoteWithTextSplit | null): void { currentQuote = rq; } diff --git a/frontend/src/ts/test/wikipedia.ts b/frontend/src/ts/test/wikipedia.ts index 5f2cf36df..5bed9a7c6 100644 --- a/frontend/src/ts/test/wikipedia.ts +++ b/frontend/src/ts/test/wikipedia.ts @@ -2,10 +2,9 @@ import * as Loader from "../elements/loader"; import * as Misc from "../utils/misc"; import * as Strings from "../utils/strings"; import * as JSONData from "../utils/json-data"; -import { Section } from "../utils/misc"; export async function getTLD( - languageGroup: MonkeyTypes.LanguageGroup + languageGroup: JSONData.LanguageGroup ): Promise< | "en" | "es" @@ -242,14 +241,14 @@ type SectionObject = { author: string; }; -export async function getSection(language: string): Promise<Section> { +export async function getSection(language: string): Promise<JSONData.Section> { // console.log("Getting section"); Loader.show(); // get TLD for wikipedia according to language group let urlTLD = "en"; - let currentLanguageGroup: MonkeyTypes.LanguageGroup | undefined; + let currentLanguageGroup: JSONData.LanguageGroup | undefined; try { currentLanguageGroup = await JSONData.getCurrentGroup(language); } catch (e) { @@ -319,7 +318,7 @@ export async function getSection(language: string): Promise<Section> { const words = sectionText.split(" "); - const section = new Section( + const section = new JSONData.Section( sectionObj.title, sectionObj.author, words diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index 822f7e693..00e31ebe2 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -2,7 +2,10 @@ import Config, * as UpdateConfig from "../config"; import * as FunboxList from "./funbox/funbox-list"; import * as CustomText from "./custom-text"; import * as Wordset from "./wordset"; -import QuotesController from "../controllers/quotes-controller"; +import QuotesController, { + Quote, + QuoteWithTextSplit, +} from "../controllers/quotes-controller"; import * as TestWords from "./test-words"; import * as BritishEnglish from "./british-english"; import * as LazyMode from "./lazy-mode"; @@ -13,6 +16,7 @@ import * as Strings from "../utils/strings"; import * as Arrays from "../utils/arrays"; import * as TestState from "../test/test-state"; import * as GetText from "../utils/generate"; +import { FunboxWordOrder, LanguageObject } from "../utils/json-data"; function shouldCapitalize(lastChar: string): boolean { return /[?!.؟]/.test(lastChar); @@ -297,9 +301,7 @@ async function applyEnglishPunctuationToWord(word: string): Promise<string> { return EnglishPunctuation.replace(word); } -function getFunboxWordsFrequency(): - | MonkeyTypes.FunboxWordsFrequency - | undefined { +function getFunboxWordsFrequency(): Wordset.FunboxWordsFrequency | undefined { const wordFunbox = FunboxList.get(Config.funbox).find( (f) => f.functions?.getWordsFrequencyMode ); @@ -372,10 +374,7 @@ async function applyBritishEnglishToWord( return await BritishEnglish.replace(word, previousWord); } -function applyLazyModeToWord( - word: string, - language: MonkeyTypes.LanguageObject -): string { +function applyLazyModeToWord(word: string, language: LanguageObject): string { const allowLazyMode = !language.noLazyMode || Config.mode === "custom"; if (Config.lazyMode && allowLazyMode) { word = LazyMode.replaceAccents(word, language.additionalAccents); @@ -383,7 +382,7 @@ function applyLazyModeToWord( return word; } -export function getWordOrder(): MonkeyTypes.FunboxWordOrder { +export function getWordOrder(): FunboxWordOrder { const wordOrder = FunboxList.get(Config.funbox) .find((f) => f.properties?.find((fp) => fp.startsWith("wordOrder"))) @@ -392,7 +391,7 @@ export function getWordOrder(): MonkeyTypes.FunboxWordOrder { if (!wordOrder) { return "normal"; } else { - return wordOrder.split(":")[1] as MonkeyTypes.FunboxWordOrder; + return wordOrder.split(":")[1] as FunboxWordOrder; } } @@ -422,7 +421,7 @@ export function getWordsLimit(): number { limit = Config.words; } if (Config.mode === "quote") { - limit = (currentQuote as MonkeyTypes.QuoteWithTextSplit).textSplit.length; + limit = (currentQuote as QuoteWithTextSplit).textSplit.length; } } @@ -456,9 +455,9 @@ export function getWordsLimit(): number { if ( Config.mode === "quote" && - (currentQuote as MonkeyTypes.QuoteWithTextSplit).textSplit.length < limit + (currentQuote as QuoteWithTextSplit).textSplit.length < limit ) { - limit = (currentQuote as MonkeyTypes.QuoteWithTextSplit).textSplit.length; + limit = (currentQuote as QuoteWithTextSplit).textSplit.length; } if ( @@ -481,8 +480,8 @@ export class WordGenError extends Error { } async function getQuoteWordList( - language: MonkeyTypes.LanguageObject, - wordOrder?: MonkeyTypes.FunboxWordOrder + language: LanguageObject, + wordOrder?: FunboxWordOrder ): Promise<string[]> { if (TestState.isRepeated) { if (currentWordset === null) { @@ -517,7 +516,7 @@ async function getQuoteWordList( ); } - let rq: MonkeyTypes.Quote; + let rq: Quote; if (Config.quoteLength.includes(-2) && Config.quoteLength.length === 1) { const targetQuote = QuotesController.getQuoteById( TestState.selectedQuoteId @@ -563,7 +562,7 @@ async function getQuoteWordList( rq.textSplit = rq.text.split(" "); } - TestWords.setCurrentQuote(rq as MonkeyTypes.QuoteWithTextSplit); + TestWords.setCurrentQuote(rq as QuoteWithTextSplit); if (TestWords.currentQuote === null) { throw new WordGenError("Random quote is null"); @@ -577,7 +576,7 @@ async function getQuoteWordList( } let currentWordset: Wordset.Wordset | null = null; -let currentLanguage: MonkeyTypes.LanguageObject | null = null; +let currentLanguage: LanguageObject | null = null; let isCurrentlyUsingFunboxSection = false; type GenerateWordsReturn = { @@ -587,10 +586,10 @@ type GenerateWordsReturn = { hasNewline: boolean; }; -let previousRandomQuote: MonkeyTypes.QuoteWithTextSplit | null = null; +let previousRandomQuote: QuoteWithTextSplit | null = null; export async function generateWords( - language: MonkeyTypes.LanguageObject + language: LanguageObject ): Promise<GenerateWordsReturn> { if (!TestState.isRepeated) { previousGetNextWordReturns = []; @@ -633,7 +632,15 @@ export async function generateWords( wordList = wordList.reverse(); } - currentWordset = await Wordset.withWords(wordList); + const wordFunbox = FunboxList.get(Config.funbox).find( + (f) => f.functions?.withWords + ); + if (wordFunbox?.functions?.withWords) { + currentWordset = await wordFunbox.functions.withWords(wordList); + } else { + currentWordset = await Wordset.withWords(wordList); + } + console.debug("Wordset", currentWordset); if (limit === 0) { @@ -675,16 +682,12 @@ export async function generateWords( ret.words.some((w) => w.includes("\t")) || currentWordset.words.some((w) => w.includes("\t")) || (Config.mode === "quote" && - (quote as MonkeyTypes.QuoteWithTextSplit).textSplit.some((w) => - w.includes("\t") - )); + (quote as QuoteWithTextSplit).textSplit.some((w) => w.includes("\t"))); ret.hasNewline = ret.words.some((w) => w.includes("\n")) || currentWordset.words.some((w) => w.includes("\n")) || (Config.mode === "quote" && - (quote as MonkeyTypes.QuoteWithTextSplit).textSplit.some((w) => - w.includes("\n") - )); + (quote as QuoteWithTextSplit).textSplit.some((w) => w.includes("\n"))); sectionHistory = []; //free up a bit of memory? is that even a thing? return ret; diff --git a/frontend/src/ts/test/wordset.ts b/frontend/src/ts/test/wordset.ts index 377f919df..4bab3c826 100644 --- a/frontend/src/ts/test/wordset.ts +++ b/frontend/src/ts/test/wordset.ts @@ -1,11 +1,11 @@ -import * as FunboxList from "./funbox/funbox-list"; import { zipfyRandomArrayIndex } from "../utils/misc"; import { randomElementFromArray, shuffle } from "../utils/arrays"; -import Config from "../config"; -let currentWordset: MonkeyTypes.Wordset | null = null; +export type FunboxWordsFrequency = "normal" | "zipf"; -export class Wordset implements MonkeyTypes.Wordset { +let currentWordset: Wordset | null = null; + +export class Wordset { words: string[]; length: number; orderedIndex: number; @@ -23,7 +23,7 @@ export class Wordset implements MonkeyTypes.Wordset { this.shuffledIndexes = []; } - randomWord(mode: MonkeyTypes.FunboxWordsFrequency): string { + randomWord(mode: FunboxWordsFrequency): string { if (mode === "zipf") { return this.words[zipfyRandomArrayIndex(this.words.length)] as string; } else { @@ -54,13 +54,7 @@ export class Wordset implements MonkeyTypes.Wordset { } } -export async function withWords(words: string[]): Promise<MonkeyTypes.Wordset> { - const wordFunbox = FunboxList.get(Config.funbox).find( - (f) => f.functions?.withWords - ); - if (wordFunbox?.functions?.withWords) { - return wordFunbox.functions.withWords(words); - } +export async function withWords(words: string[]): Promise<Wordset> { if (currentWordset === null || words !== currentWordset.words) { currentWordset = new Wordset(words); } diff --git a/frontend/src/ts/types/types.d.ts b/frontend/src/ts/types/types.d.ts deleted file mode 100644 index 54f008bd3..000000000 --- a/frontend/src/ts/types/types.d.ts +++ /dev/null @@ -1,510 +0,0 @@ -type Mode = import("@monkeytype/contracts/schemas/shared").Mode; -type Result<M extends Mode> = - import("@monkeytype/contracts/schemas/results").Result<M>; -type IncompleteTest = - import("@monkeytype/contracts/schemas/results").IncompleteTest; - -declare namespace MonkeyTypes { - type PageName = - | "loading" - | "test" - | "settings" - | "about" - | "account" - | "login" - | "profile" - | "profileSearch" - | "404" - | "accountSettings"; - - type LanguageGroup = { - name: string; - languages: string[]; - }; - - type AddNotificationOptions = { - important?: boolean; - duration?: number; - customTitle?: string; - customIcon?: string; - closeCallback?: () => void; - allowHTML?: boolean; - }; - - type Accents = [string, string][]; - - type LanguageObject = { - name: string; - rightToLeft: boolean; - noLazyMode?: boolean; - ligatures?: boolean; - orderedByFrequency?: boolean; - words: string[]; - additionalAccents: Accents; - bcp47?: string; - originalPunctuation?: boolean; - }; - - type DefaultWordsModes = 10 | 25 | 50 | 100; - - type DefaultTimeModes = 15 | 30 | 60 | 120; - - type QuoteModes = "short" | "medium" | "long" | "thicc"; - - type CustomLayoutFluidSpaces = - | import("@monkeytype/contracts/schemas/configs").CustomLayoutFluid - | `${string} ${string} ${string}`; - - type HistoryChartData = { - x: number; - y: number; - wpm: number; - acc: number; - mode: string; - mode2: string; - punctuation: boolean; - language: string; - timestamp: number; - difficulty: string; - raw: number; - isPb: boolean; - }; - - type AccChartData = { - x: number; - y: number; - errorRate: number; - }; - - type OtherChartData = { - x: number; - y: number; - }; - - type ActivityChartDataPoint = { - x: number; - y: number; - amount?: number; - }; - - type FontObject = { - name: string; - display?: string; - systemFont?: string; - }; - - type FunboxWordsFrequency = "normal" | "zipf"; - - type FunboxWordOrder = "normal" | "reverse"; - - type FunboxProperty = - | "symmetricChars" - | "conflictsWithSymmetricChars" - | "changesWordsVisibility" - | "speaks" - | "unspeakable" - | "changesLayout" - | "ignoresLayout" - | "usesLayout" - | "ignoresLanguage" - | "noLigatures" - | "noLetters" - | "changesCapitalisation" - | "nospace" - | `toPush:${number}` - | "noInfiniteDuration" - | "changesWordsFrequency" - | `wordOrder:${FunboxWordOrder}`; - - class Wordset { - words: string[]; - length: number; - orderedIndex: number; - shuffledIndexes: number[]; - constructor(words: string[]); - resetIndexes(): void; - randomWord(mode: MonkeyTypes.FunboxWordsFrequency): string; - shuffledWord(): string; - generateShuffledIndexes(): void; - nextWord(): string; - } - - class Section { - public title: string; - public author: string; - public words: string[]; - constructor(title: string, author: string, words: string[]); - } - - type FunboxFunctions = { - getWord?: (wordset?: Wordset, wordIndex?: number) => string; - punctuateWord?: (word: string) => string; - withWords?: (words?: string[]) => Promise<Wordset>; - alterText?: (word: string) => string; - applyConfig?: () => void; - applyGlobalCSS?: () => void; - clearGlobal?: () => void; - rememberSettings?: () => void; - toggleScript?: (params: string[]) => void; - pullSection?: (language?: string) => Promise<Section | false>; - handleSpace?: () => void; - handleChar?: (char: string) => string; - isCharCorrect?: (char: string, originalChar: string) => boolean; - preventDefaultEvent?: ( - event: JQuery.KeyDownEvent<Document, null, Document, Document> - ) => Promise<boolean>; - handleKeydown?: ( - event: JQuery.KeyDownEvent<Document, null, Document, Document> - ) => Promise<void>; - getResultContent?: () => string; - start?: () => void; - restart?: () => void; - getWordHtml?: (char: string, letterTag?: boolean) => string; - getWordsFrequencyMode?: () => FunboxWordsFrequency; - }; - - type FunboxForcedConfig = Record< - string, - import("@monkeytype/contracts/schemas/configs").ConfigValue[] - >; - - type FunboxMetadata = { - name: string; - info: string; - canGetPb?: boolean; - alias?: string; - forcedConfig?: MonkeyTypes.FunboxForcedConfig; - properties?: FunboxProperty[]; - functions?: FunboxFunctions; - hasCSS?: boolean; - }; - - type SnapshotPreset = - import("@monkeytype/contracts/schemas/presets").Preset & { - display: string; - }; - - type RawCustomTheme = { - name: string; - colors: import("@monkeytype/contracts/schemas/configs").CustomThemeColors; - }; - - type CustomTheme = { - _id: string; - } & RawCustomTheme; - - type ConfigChanges = Partial< - import("@monkeytype/contracts/schemas/configs").Config - >; - - type LeaderboardMemory = { - time: { - [_key in "15" | "60"]: Record<string, number>; - }; - }; - - type QuoteRatings = Record<string, Record<number, number>>; - - type UserTag = import("@monkeytype/contracts/schemas/users").UserTag & { - active?: boolean; - display: string; - }; - - type Snapshot = Omit< - import("@monkeytype/contracts/schemas/users").User, - | "timeTyping" - | "startedTests" - | "completedTests" - | "profileDetails" - | "streak" - | "resultFilterPresets" - | "tags" - | "xp" - | "testActivity" - > & { - typingStats: { - timeTyping: number; - startedTests: number; - completedTests: number; - }; - details?: import("@monkeytype/contracts/schemas/users").UserProfileDetails; - inboxUnreadSize: number; - streak: number; - maxStreak: number; - filterPresets: import("@monkeytype/contracts/schemas/users").ResultFilters[]; - isPremium: boolean; - streakHourOffset?: number; - config: import("@monkeytype/contracts/schemas/configs").Config; - tags: UserTag[]; - presets: SnapshotPreset[]; - results?: MonkeyTypes.FullResult<Mode>[]; - xp: number; - testActivity?: ModifiableTestActivityCalendar; - testActivityByYear?: { [key: string]: TestActivityCalendar }; - }; - - type TimerStats = { - dateNow: number; - now: number; - expected: number; - nextDelay: number; - }; - - type GithubRelease = { - url: string; - assets_url: string; - upload_url: string; - html_url: string; - id: number; - author: { - login: string; - id: number; - node_id: string; - avatar_url: string; - gravatar_id: string; - url: string; - html_url: string; - followers_url: string; - following_url: string; - gists_url: string; - starred_url: string; - subscriptions_url: string; - organizations_url: string; - repos_url: string; - events_url: string; - received_events_url: string; - type: string; - site_admin: boolean; - }; - node_id: string; - tag_name: string; - target_commitish: string; - name: string; - draft: boolean; - prerelease: boolean; - created_at: string; - published_at: string; - assets: unknown[]; - tarball_url: string; - zipball_url: string; - body: string; - reactions: { - url: string; - total_count: number; - [reaction: string]: number | string; - }; - }; - - type CommandExecOptions = { - input?: string; - commandlineModal: import("../utils/animated-modal").default; - }; - - type Command = { - id: string; - display: string; - singleListDisplay?: string; - singleListDisplayNoIcon?: string; - subgroup?: CommandsSubgroup; - found?: boolean; - icon?: string; - sticky?: boolean; - alias?: string; - input?: boolean; - visible?: boolean; - customStyle?: string; - opensModal?: boolean; - defaultValue?: () => string; - configKey?: keyof import("@monkeytype/contracts/schemas/configs").Config; - configValue?: string | number | boolean | number[]; - configValueMode?: "include"; - exec?: (options: CommandExecOptions) => void; - hover?: () => void; - available?: () => boolean; - active?: () => boolean; - shouldFocusTestUI?: boolean; - customData?: Record<string, string | boolean>; - }; - - type CommandsSubgroup = { - title: string; - configKey?: keyof import("@monkeytype/contracts/schemas/configs").Config; - list: Command[]; - beforeList?: () => void; - }; - - type Theme = { - name: string; - bgColor: string; - mainColor: string; - subColor: string; - textColor: string; - }; - - type Quote = { - text: string; - britishText?: string; - source: string; - length: number; - id: number; - group: number; - language: string; - textSplit?: string[]; - }; - - type QuoteWithTextSplit = Quote & { - textSplit: string[]; - }; - - type ThemeColors = { - bg: string; - main: string; - caret: string; - sub: string; - subAlt: string; - text: string; - error: string; - errorExtra: string; - colorfulError: string; - colorfulErrorExtra: string; - }; - - type Layout = { - keymapShowTopRow: boolean; - matrixShowRightColumn?: boolean; - type: "iso" | "ansi" | "ortho" | "matrix"; - keys: Keys; - }; - - type Layouts = Record<string, Layout>; - type Keys = { - row1: string[]; - row2: string[]; - row3: string[]; - row4: string[]; - row5: string[]; - }; - - type WpmAndRaw = { - wpm: number; - raw: number; - }; - - type Challenge = { - name: string; - display: string; - autoRole: boolean; - type: string; - parameters: (string | number | boolean)[]; - message: string; - requirements: Record<string, Record<string, string | number | boolean>>; - }; - - type UserBadge = { - id: number; - name: string; - description: string; - icon?: string; - background?: string; - color?: string; - customStyle?: string; - }; - - type MonkeyMail = { - id: string; - subject: string; - body: string; - timestamp: number; - read: boolean; - rewards: AllRewards[]; - }; - - type Reward<T> = { - type: string; - item: T; - }; - - type XpReward = { - type: "xp"; - item: number; - } & Reward<number>; - - type BadgeReward = { - type: "badge"; - item: import("@monkeytype/contracts/schemas/users").Badge; - } & Reward<import("@monkeytype/contracts/schemas/users").Badge>; - - type AllRewards = XpReward | BadgeReward; - - type TypingSpeedUnitSettings = { - fromWpm: (number: number) => number; - toWpm: (number: number) => number; - fullUnitString: string; - histogramDataBucketSize: number; - historyStepSize: number; - }; - - type TestActivityCalendar = { - getMonths: () => TestActivityMonth[]; - getDays: () => TestActivityDay[]; - getTotalTests: () => number; - }; - - type ModifiableTestActivityCalendar = TestActivityCalendar & { - increment: (date: Date) => void; - getFullYearCalendar: () => TestActivityCalendar; - }; - - type TestActivityDay = { - level: string; - label?: string; - }; - - type TestActivityMonth = { - text: string; - weeks: number; - }; - - /** - * Result from the rest api but all omittable default values are set (and non optional) - */ - type FullResult<M extends Mode> = Omit< - Result<M>, - | "restartCount" - | "incompleteTestSeconds" - | "afkDuration" - | "tags" - | "bailedOut" - | "blindMode" - | "lazyMode" - | "funbox" - | "language" - | "difficulty" - | "numbers" - | "punctuation" - > & { - restartCount: number; - incompleteTestSeconds: number; - afkDuration: number; - tags: string[]; - bailedOut: boolean; - blindMode: boolean; - lazyMode: boolean; - funbox: string; - language: string; - difficulty: import("@monkeytype/contracts/schemas/shared").Difficulty; - numbers: boolean; - punctuation: boolean; - }; - type CustomTextLimit = { - value: number; - mode: import("@monkeytype/contracts/schemas/util").CustomTextLimitMode; - }; - - type CustomTextData = Omit< - import("@monkeytype/contracts/schemas/results").CustomTextDataWithTextLen, - "textLen" - > & { - text: string[]; - }; -} diff --git a/frontend/src/ts/utils/json-data.ts b/frontend/src/ts/utils/json-data.ts index e2898e5d2..79909d5c4 100644 --- a/frontend/src/ts/utils/json-data.ts +++ b/frontend/src/ts/utils/json-data.ts @@ -1,4 +1,7 @@ +import { ConfigValue } from "@monkeytype/contracts/schemas/configs"; +import { Accents } from "../test/lazy-mode"; import { hexToHSL } from "./colors"; +import { FunboxWordsFrequency, Wordset } from "../test/wordset"; /** * Fetches JSON data from the specified URL using the fetch API. @@ -62,13 +65,30 @@ export const cachedFetchJson = memoizeAsync<string, typeof fetchJson>( fetchJson ); +export type Keys = { + row1: string[]; + row2: string[]; + row3: string[]; + row4: string[]; + row5: string[]; +}; + +export type Layout = { + keymapShowTopRow: boolean; + matrixShowRightColumn?: boolean; + type: "iso" | "ansi" | "ortho" | "matrix"; + keys: Keys; +}; + +export type LayoutsList = Record<string, Layout>; + /** * Fetches the layouts list from the server. * @returns A promise that resolves to the layouts list. */ -export async function getLayoutsList(): Promise<MonkeyTypes.Layouts> { +export async function getLayoutsList(): Promise<LayoutsList> { try { - const layoutsList = await cachedFetchJson<MonkeyTypes.Layouts>( + const layoutsList = await cachedFetchJson<LayoutsList>( "/layouts/_list.json" ); return layoutsList; @@ -83,9 +103,7 @@ export async function getLayoutsList(): Promise<MonkeyTypes.Layouts> { * @returns A promise that resolves to the layout object. * @throws {Error} If the layout list or layout doesn't exist. */ -export async function getLayout( - layoutName: string -): Promise<MonkeyTypes.Layout> { +export async function getLayout(layoutName: string): Promise<Layout> { const layouts = await getLayoutsList(); const layout = layouts[layoutName]; if (layout === undefined) { @@ -94,20 +112,26 @@ export async function getLayout( return layout; } -let themesList: MonkeyTypes.Theme[] | undefined; +export type Theme = { + name: string; + bgColor: string; + mainColor: string; + subColor: string; + textColor: string; +}; + +let themesList: Theme[] | undefined; /** * Fetches the list of themes from the server, sorting them alphabetically by name. * If the list has already been fetched, returns the cached list. * @returns A promise that resolves to the sorted list of themes. */ -export async function getThemesList(): Promise<MonkeyTypes.Theme[]> { +export async function getThemesList(): Promise<Theme[]> { if (!themesList) { - let themes = await cachedFetchJson<MonkeyTypes.Theme[]>( - "/themes/_list.json" - ); + let themes = await cachedFetchJson<Theme[]>("/themes/_list.json"); - themes = themes.sort(function (a: MonkeyTypes.Theme, b: MonkeyTypes.Theme) { + themes = themes.sort((a, b) => { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); if (nameA < nameB) return -1; @@ -121,13 +145,13 @@ export async function getThemesList(): Promise<MonkeyTypes.Theme[]> { } } -let sortedThemesList: MonkeyTypes.Theme[] | undefined; +let sortedThemesList: Theme[] | undefined; /** * Fetches the sorted list of themes from the server. * @returns A promise that resolves to the sorted list of themes. */ -export async function getSortedThemesList(): Promise<MonkeyTypes.Theme[]> { +export async function getSortedThemesList(): Promise<Theme[]> { if (!sortedThemesList) { if (!themesList) { await getThemesList(); @@ -163,42 +187,64 @@ export async function getLanguageList(): Promise<string[]> { } } +export type LanguageGroup = { + name: string; + languages: string[]; +}; + /** * Fetches the list of language groups from the server. * @returns A promise that resolves to the list of language groups. */ -export async function getLanguageGroups(): Promise< - MonkeyTypes.LanguageGroup[] -> { +export async function getLanguageGroups(): Promise<LanguageGroup[]> { try { - const languageGroupList = await cachedFetchJson< - MonkeyTypes.LanguageGroup[] - >("/languages/_groups.json"); + const languageGroupList = await cachedFetchJson<LanguageGroup[]>( + "/languages/_groups.json" + ); return languageGroupList; } catch (e) { throw new Error("Language groups JSON fetch failed"); } } -let currentLanguage: MonkeyTypes.LanguageObject; +export type LanguageObject = { + name: string; + rightToLeft: boolean; + noLazyMode?: boolean; + ligatures?: boolean; + orderedByFrequency?: boolean; + words: string[]; + additionalAccents: Accents; + bcp47?: string; + originalPunctuation?: boolean; +}; + +let currentLanguage: LanguageObject; /** * Fetches the language object for a given language from the server. * @param lang The language code. * @returns A promise that resolves to the language object. */ -export async function getLanguage( - lang: string -): Promise<MonkeyTypes.LanguageObject> { +export async function getLanguage(lang: string): Promise<LanguageObject> { // try { if (currentLanguage === undefined || currentLanguage.name !== lang) { - currentLanguage = await cachedFetchJson<MonkeyTypes.LanguageObject>( + currentLanguage = await cachedFetchJson<LanguageObject>( `/languages/${lang}.json` ); } return currentLanguage; } +export async function checkIfLanguageSupportsZipf( + language: string +): Promise<"yes" | "no" | "unknown"> { + const lang = await getLanguage(language); + if (lang.orderedByFrequency === true) return "yes"; + if (lang.orderedByFrequency === false) return "no"; + return "unknown"; +} + /** * Fetches the current language object. * @param languageName The name of the language. @@ -206,7 +252,7 @@ export async function getLanguage( */ export async function getCurrentLanguage( languageName: string -): Promise<MonkeyTypes.LanguageObject> { +): Promise<LanguageObject> { return await getLanguage(languageName); } @@ -217,8 +263,8 @@ export async function getCurrentLanguage( */ export async function getCurrentGroup( language: string -): Promise<MonkeyTypes.LanguageGroup | undefined> { - let retgroup: MonkeyTypes.LanguageGroup | undefined; +): Promise<LanguageGroup | undefined> { + let retgroup: LanguageGroup | undefined; const groups = await getLanguageGroups(); groups.forEach((group) => { if (retgroup === undefined) { @@ -230,21 +276,16 @@ export async function getCurrentGroup( return retgroup; } -let funboxList: MonkeyTypes.FunboxMetadata[] | undefined; +let funboxList: FunboxMetadata[] | undefined; /** * Fetches the list of funbox metadata from the server. * @returns A promise that resolves to the list of funbox metadata. */ -export async function getFunboxList(): Promise<MonkeyTypes.FunboxMetadata[]> { +export async function getFunboxList(): Promise<FunboxMetadata[]> { if (!funboxList) { - let list = await cachedFetchJson<MonkeyTypes.FunboxMetadata[]>( - "/funbox/_list.json" - ); - list = list.sort(function ( - a: MonkeyTypes.FunboxMetadata, - b: MonkeyTypes.FunboxMetadata - ) { + let list = await cachedFetchJson<FunboxMetadata[]>("/funbox/_list.json"); + list = list.sort((a, b) => { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); if (nameA < nameB) return -1; @@ -258,6 +299,78 @@ export async function getFunboxList(): Promise<MonkeyTypes.FunboxMetadata[]> { } } +export class Section { + public title: string; + public author: string; + public words: string[]; + constructor(title: string, author: string, words: string[]) { + this.title = title; + this.author = author; + this.words = words; + } +} + +export type FunboxMetadata = { + name: string; + info: string; + canGetPb?: boolean; + alias?: string; + forcedConfig?: FunboxForcedConfig; + properties?: FunboxProperty[]; + functions?: FunboxFunctions; + hasCSS?: boolean; +}; + +export type FunboxWordOrder = "normal" | "reverse"; + +type FunboxProperty = + | "symmetricChars" + | "conflictsWithSymmetricChars" + | "changesWordsVisibility" + | "speaks" + | "unspeakable" + | "changesLayout" + | "ignoresLayout" + | "usesLayout" + | "ignoresLanguage" + | "noLigatures" + | "noLetters" + | "changesCapitalisation" + | "nospace" + | `toPush:${number}` + | "noInfiniteDuration" + | "changesWordsFrequency" + | `wordOrder:${FunboxWordOrder}`; + +export type FunboxForcedConfig = Record<string, ConfigValue[]>; + +export type FunboxFunctions = { + getWord?: (wordset?: Wordset, wordIndex?: number) => string; + punctuateWord?: (word: string) => string; + withWords?: (words?: string[]) => Promise<Wordset>; + alterText?: (word: string) => string; + applyConfig?: () => void; + applyGlobalCSS?: () => void; + clearGlobal?: () => void; + rememberSettings?: () => void; + toggleScript?: (params: string[]) => void; + pullSection?: (language?: string) => Promise<Section | false>; + handleSpace?: () => void; + handleChar?: (char: string) => string; + isCharCorrect?: (char: string, originalChar: string) => boolean; + preventDefaultEvent?: ( + event: JQuery.KeyDownEvent<Document, null, Document, Document> + ) => Promise<boolean>; + handleKeydown?: ( + event: JQuery.KeyDownEvent<Document, null, Document, Document> + ) => Promise<void>; + getResultContent?: () => string; + start?: () => void; + restart?: () => void; + getWordHtml?: (char: string, letterTag?: boolean) => string; + getWordsFrequencyMode?: () => FunboxWordsFrequency; +}; + /** * Fetches the funbox metadata for a given funbox from the server. * @param funbox The name of the funbox. @@ -265,28 +378,29 @@ export async function getFunboxList(): Promise<MonkeyTypes.FunboxMetadata[]> { */ export async function getFunbox( funbox: string -): Promise<MonkeyTypes.FunboxMetadata | undefined> { - const list: MonkeyTypes.FunboxMetadata[] = await getFunboxList(); +): Promise<FunboxMetadata | undefined> { + const list: FunboxMetadata[] = await getFunboxList(); return list.find(function (element) { return element.name === funbox; }); } -let fontsList: MonkeyTypes.FontObject[] | undefined; +export type FontObject = { + name: string; + display?: string; + systemFont?: string; +}; + +let fontsList: FontObject[] | undefined; /** * Fetches the list of font objects from the server. * @returns A promise that resolves to the list of font objects. */ -export async function getFontsList(): Promise<MonkeyTypes.FontObject[]> { +export async function getFontsList(): Promise<FontObject[]> { if (!fontsList) { - let list = await cachedFetchJson<MonkeyTypes.FontObject[]>( - "/fonts/_list.json" - ); - list = list.sort(function ( - a: MonkeyTypes.FontObject, - b: MonkeyTypes.FontObject - ) { + let list = await cachedFetchJson<FontObject[]>("/fonts/_list.json"); + list = list.sort((a, b) => { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); if (nameA < nameB) return -1; @@ -300,15 +414,23 @@ export async function getFontsList(): Promise<MonkeyTypes.FontObject[]> { } } +export type Challenge = { + name: string; + display: string; + autoRole: boolean; + type: string; + parameters: (string | number | boolean)[]; + message: string; + requirements: Record<string, Record<string, string | number | boolean>>; +}; + /** * Fetches the list of challenges from the server. * @returns A promise that resolves to the list of challenges. */ -export async function getChallengeList(): Promise<MonkeyTypes.Challenge[]> { +export async function getChallengeList(): Promise<Challenge[]> { try { - const data = await cachedFetchJson<MonkeyTypes.Challenge[]>( - "/challenges/_list.json" - ); + const data = await cachedFetchJson<Challenge[]>("/challenges/_list.json"); return data; } catch (e) { throw new Error("Challenge list JSON fetch failed"); @@ -341,6 +463,51 @@ export async function getContributorsList(): Promise<string[]> { } } +type GithubRelease = { + url: string; + assets_url: string; + upload_url: string; + html_url: string; + id: number; + author: { + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; + }; + node_id: string; + tag_name: string; + target_commitish: string; + name: string; + draft: boolean; + prerelease: boolean; + created_at: string; + published_at: string; + assets: unknown[]; + tarball_url: string; + zipball_url: string; + body: string; + reactions: { + url: string; + total_count: number; + [reaction: string]: number | string; + }; +}; + /** * Fetches the latest release name from GitHub. * @returns A promise that resolves to the latest release name. @@ -360,9 +527,7 @@ export async function getLatestReleaseFromGitHub(): Promise<string> { * Fetches the list of releases from GitHub. * @returns A promise that resolves to the list of releases. */ -export async function getReleasesFromGitHub(): Promise< - MonkeyTypes.GithubRelease[] -> { +export async function getReleasesFromGitHub(): Promise<GithubRelease[]> { return cachedFetchJson( "https://api.github.com/repos/monkeytypegame/monkeytype/releases?per_page=5" ); diff --git a/frontend/src/ts/utils/misc.ts b/frontend/src/ts/utils/misc.ts index bc785a172..82651157c 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -1,7 +1,6 @@ import * as Loader from "../elements/loader"; import { envConfig } from "../constants/env-config"; import { lastElementFromArray } from "./arrays"; -import * as JSONData from "./json-data"; import { Config } from "@monkeytype/contracts/schemas/configs"; import { Mode, @@ -9,6 +8,10 @@ import { PersonalBests, } from "@monkeytype/contracts/schemas/shared"; import { ZodError, ZodSchema } from "zod"; +import { + CustomTextDataWithTextLen, + Result, +} from "@monkeytype/contracts/schemas/results"; export function whorf(speed: number, wordlen: number): number { return Math.min( @@ -196,7 +199,7 @@ export function canQuickRestart( mode: string, words: number, time: number, - CustomText: MonkeyTypes.CustomTextData, + CustomText: Omit<CustomTextDataWithTextLen, "textLen">, customTextIsLong: boolean ): boolean { const wordsLong = mode === "words" && (words >= 1000 || words === 0); @@ -357,7 +360,7 @@ export async function swapElements( export function getMode2<M extends keyof PersonalBests>( config: Config, - randomQuote: MonkeyTypes.Quote | null + randomQuote: { id: number } | null ): Mode2<M> { const mode = config.mode; let retVal: string; @@ -379,9 +382,7 @@ export function getMode2<M extends keyof PersonalBests>( return retVal as Mode2<M>; } -export async function downloadResultsCSV( - array: MonkeyTypes.FullResult<Mode>[] -): Promise<void> { +export async function downloadResultsCSV(array: Result<Mode>[]): Promise<void> { Loader.show(); const csvString = [ [ @@ -410,7 +411,7 @@ export async function downloadResultsCSV( "tags", "timestamp", ], - ...array.map((item: MonkeyTypes.FullResult<Mode>) => [ + ...array.map((item) => [ item._id, item.isPb, item.wpm, @@ -433,7 +434,7 @@ export async function downloadResultsCSV( item.lazyMode, item.blindMode, item.bailedOut, - item.tags.join(";"), + item.tags?.join(";"), item.timestamp, ]), ] @@ -532,17 +533,6 @@ export async function sleep(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } -export class Section { - public title: string; - public author: string; - public words: string[]; - constructor(title: string, author: string, words: string[]) { - this.title = title; - this.author = author; - this.words = words; - } -} - export function isPasswordStrong(password: string): boolean { const hasCapital = !!password.match(/[A-Z]/); const hasNumber = !!password.match(/[\d]/); @@ -597,15 +587,6 @@ export function zipfyRandomArrayIndex(dictLength: number): number { return Math.floor(inverseCDF); } -export async function checkIfLanguageSupportsZipf( - language: string -): Promise<"yes" | "no" | "unknown"> { - const lang = await JSONData.getLanguage(language); - if (lang.orderedByFrequency === true) return "yes"; - if (lang.orderedByFrequency === false) return "no"; - return "unknown"; -} - // Function to get the bounding rectangle of a collection of elements export function getBoundingRectOfElements(elements: HTMLElement[]): DOMRect { let minX = Infinity, diff --git a/frontend/src/ts/utils/results.ts b/frontend/src/ts/utils/results.ts index ab04f2e58..cb065eec6 100644 --- a/frontend/src/ts/utils/results.ts +++ b/frontend/src/ts/utils/results.ts @@ -2,6 +2,8 @@ import Ape from "../ape"; import * as Notifications from "../elements/notifications"; import * as DB from "../db"; import * as TestLogic from "../test/test-logic"; +import { deepClone } from "./misc"; +import { Mode } from "@monkeytype/contracts/schemas/shared"; export async function syncNotSignedInLastResult(uid: string): Promise<void> { const notSignedInLastResult = TestLogic.notSignedInLastResult; @@ -19,9 +21,12 @@ export async function syncNotSignedInLastResult(uid: string): Promise<void> { return; } - const result = JSON.parse( - JSON.stringify(notSignedInLastResult) - ) as MonkeyTypes.FullResult<Mode>; + //TODO - this type cast was not needed before because we were using JSON cloning + // but now with the stronger types it shows that we are forcing completed event + // into a snapshot result - might not cuase issues but worth investigating + const result = deepClone( + notSignedInLastResult + ) as unknown as DB.SnapshotResult<Mode>; result._id = response.body.data.insertedId; if (response.body.data.isPb) { result.isPb = true; diff --git a/frontend/src/ts/utils/simple-modal.ts b/frontend/src/ts/utils/simple-modal.ts index e9927a7e3..6763627fa 100644 --- a/frontend/src/ts/utils/simple-modal.ts +++ b/frontend/src/ts/utils/simple-modal.ts @@ -62,7 +62,7 @@ export type ExecReturn = { status: 1 | 0 | -1; message: string; showNotification?: false; - notificationOptions?: MonkeyTypes.AddNotificationOptions; + notificationOptions?: Notifications.AddNotificationOptions; hideOptions?: HideOptions; afterHide?: () => void; }; diff --git a/frontend/src/ts/utils/typing-speed-units.ts b/frontend/src/ts/utils/typing-speed-units.ts index 9d0f8bb4e..214b5a5ea 100644 --- a/frontend/src/ts/utils/typing-speed-units.ts +++ b/frontend/src/ts/utils/typing-speed-units.ts @@ -1,6 +1,14 @@ import { TypingSpeedUnit } from "@monkeytype/contracts/schemas/configs"; -class Unit implements MonkeyTypes.TypingSpeedUnitSettings { +type TypingSpeedUnitSettings = { + fromWpm: (number: number) => number; + toWpm: (number: number) => number; + fullUnitString: string; + histogramDataBucketSize: number; + historyStepSize: number; +}; + +class Unit implements TypingSpeedUnitSettings { unit: TypingSpeedUnit; convertFactor: number; fullUnitString: string; @@ -38,8 +46,6 @@ const typingSpeedUnits: Record<TypingSpeedUnit, Unit> = { wph: new Unit("wph", 60, "Words per Hour", 250, 1000), }; -export function get( - unit: TypingSpeedUnit -): MonkeyTypes.TypingSpeedUnitSettings { +export function get(unit: TypingSpeedUnit): TypingSpeedUnitSettings { return typingSpeedUnits[unit]; } diff --git a/frontend/src/ts/utils/url-handler.ts b/frontend/src/ts/utils/url-handler.ts index dc05b8c63..7ba713e49 100644 --- a/frontend/src/ts/utils/url-handler.ts +++ b/frontend/src/ts/utils/url-handler.ts @@ -134,7 +134,7 @@ export function loadCustomThemeFromUrl(getOverride?: string): void { type SharedTestSettings = [ Mode | null, Mode2<Mode> | null, - MonkeyTypes.CustomTextData | null, + CustomText.CustomTextData | null, boolean | null, boolean | null, string | null, diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index a74452381..d4de7464f 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -9,10 +9,6 @@ "target": "ES6", "noEmit": true }, - "include": [ - "./src/**/*.ts", - "./scripts/**/*.ts", - "./src/ts/ape/types/*.d.ts" - ], + "include": ["./src/**/*.ts", "./scripts/**/*.ts"], "exclude": ["node_modules", "build", "setup-tests.ts", "**/*.spec.ts"] } diff --git a/packages/contracts/src/schemas/shared.ts b/packages/contracts/src/schemas/shared.ts index e80e9a2d8..df10f0363 100644 --- a/packages/contracts/src/schemas/shared.ts +++ b/packages/contracts/src/schemas/shared.ts @@ -39,7 +39,34 @@ export const PersonalBestsSchema = z.object({ }); export type PersonalBests = z.infer<typeof PersonalBestsSchema>; -//used by user, config, public +export const DefaultWordsModeSchema = z.union([ + z.literal("10"), + z.literal("25"), + z.literal("50"), + z.literal("100"), +]); + +export const DefaultTimeModeSchema = z.union([ + z.literal("15"), + z.literal("30"), + z.literal("60"), + z.literal("120"), +]); + +export const QuoteLengthSchema = z.union([ + z.literal("short"), + z.literal("medium"), + z.literal("long"), + z.literal("thicc"), +]); + +// // Step 1: Define the schema for specific string values "10" and "25" +// const SpecificKeySchema = z.union([z.literal("10"), z.literal("25")]); + +// // Step 2: Use this schema as the key schema for another object +// export const ExampleSchema = z.record(SpecificKeySchema, z.string()); + +// //used by user, config, public export const ModeSchema = PersonalBestsSchema.keyof(); export type Mode = z.infer<typeof ModeSchema>; diff --git a/packages/contracts/src/schemas/users.ts b/packages/contracts/src/schemas/users.ts index 9bcf3c417..d1f986fd4 100644 --- a/packages/contracts/src/schemas/users.ts +++ b/packages/contracts/src/schemas/users.ts @@ -1,6 +1,14 @@ import { z, ZodEffects, ZodOptional, ZodString } from "zod"; import { IdSchema, LanguageSchema, StringNumberSchema } from "./util"; -import { ModeSchema, Mode2Schema, PersonalBestsSchema } from "./shared"; +import { + ModeSchema, + Mode2Schema, + PersonalBestsSchema, + DefaultWordsModeSchema, + DefaultTimeModeSchema, + QuoteLengthSchema, + DifficultySchema, +} from "./shared"; import { CustomThemeColorsSchema } from "./configs"; import { doesNotContainProfanity } from "../validation/validation"; @@ -16,40 +24,11 @@ export const ResultFiltersSchema = z.object({ yes: z.boolean(), }) .strict(), - difficulty: z - .object({ - normal: z.boolean(), - expert: z.boolean(), - master: z.boolean(), - }) - .strict(), + difficulty: z.record(DifficultySchema, z.boolean()), mode: z.record(ModeSchema, z.boolean()), - words: z - .object({ - "10": z.boolean(), - "25": z.boolean(), - "50": z.boolean(), - "100": z.boolean(), - custom: z.boolean(), - }) - .strict(), - time: z - .object({ - "15": z.boolean(), - "30": z.boolean(), - "60": z.boolean(), - "120": z.boolean(), - custom: z.boolean(), - }) - .strict(), - quoteLength: z - .object({ - short: z.boolean(), - medium: z.boolean(), - long: z.boolean(), - thicc: z.boolean(), - }) - .strict(), + words: z.record(DefaultWordsModeSchema.or(z.literal("custom")), z.boolean()), + time: z.record(DefaultTimeModeSchema.or(z.literal("custom")), z.boolean()), + quoteLength: z.record(QuoteLengthSchema, z.boolean()), punctuation: z .object({ on: z.boolean(), |