mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-02-01 19:38:24 +08:00
c95e3b2fa8
* feat: indicate premium users * frontend * Test multiple userFlags, remove later * cleanup * fix flag alignment on profile and leaderboards * fix name auto scaling * update screenshot watermark * update header text * use userFlags for lbOptOut * use flex end * removeo unused code, increase margin --------- Co-authored-by: Miodec <jack@monkeytype.com>
604 lines
14 KiB
TypeScript
604 lines
14 KiB
TypeScript
// Shared types between server/client.
|
|
declare namespace SharedTypes {
|
|
interface ValidModeRule {
|
|
language: string;
|
|
mode: string;
|
|
mode2: string;
|
|
}
|
|
interface RewardBracket {
|
|
minRank: number;
|
|
maxRank: number;
|
|
minReward: number;
|
|
maxReward: number;
|
|
}
|
|
|
|
interface Configuration {
|
|
maintenance: boolean;
|
|
quotes: {
|
|
reporting: {
|
|
enabled: boolean;
|
|
maxReports: number;
|
|
contentReportLimit: number;
|
|
};
|
|
submissionsEnabled: boolean;
|
|
maxFavorites: number;
|
|
};
|
|
results: {
|
|
savingEnabled: boolean;
|
|
objectHashCheckEnabled: boolean;
|
|
filterPresets: {
|
|
enabled: boolean;
|
|
maxPresetsPerUser: number;
|
|
};
|
|
limits: {
|
|
regularUser: number;
|
|
premiumUser: number;
|
|
};
|
|
maxBatchSize: number;
|
|
};
|
|
users: {
|
|
signUp: boolean;
|
|
lastHashesCheck: {
|
|
enabled: boolean;
|
|
maxHashes: number;
|
|
};
|
|
autoBan: {
|
|
enabled: boolean;
|
|
maxCount: number;
|
|
maxHours: number;
|
|
};
|
|
profiles: {
|
|
enabled: boolean;
|
|
};
|
|
discordIntegration: {
|
|
enabled: boolean;
|
|
};
|
|
xp: {
|
|
enabled: boolean;
|
|
funboxBonus: number;
|
|
gainMultiplier: number;
|
|
maxDailyBonus: number;
|
|
minDailyBonus: number;
|
|
streak: {
|
|
enabled: boolean;
|
|
maxStreakDays: number;
|
|
maxStreakMultiplier: number;
|
|
};
|
|
};
|
|
inbox: {
|
|
enabled: boolean;
|
|
maxMail: number;
|
|
};
|
|
premium: {
|
|
enabled: boolean;
|
|
};
|
|
};
|
|
admin: {
|
|
endpointsEnabled: boolean;
|
|
};
|
|
apeKeys: {
|
|
endpointsEnabled: boolean;
|
|
acceptKeys: boolean;
|
|
maxKeysPerUser: number;
|
|
apeKeyBytes: number;
|
|
apeKeySaltRounds: number;
|
|
};
|
|
rateLimiting: {
|
|
badAuthentication: {
|
|
enabled: boolean;
|
|
penalty: number;
|
|
flaggedStatusCodes: number[];
|
|
};
|
|
};
|
|
dailyLeaderboards: {
|
|
enabled: boolean;
|
|
leaderboardExpirationTimeInDays: number;
|
|
maxResults: number;
|
|
validModeRules: ValidModeRule[];
|
|
scheduleRewardsModeRules: ValidModeRule[];
|
|
topResultsToAnnounce: number;
|
|
xpRewardBrackets: RewardBracket[];
|
|
};
|
|
leaderboards: {
|
|
weeklyXp: {
|
|
enabled: boolean;
|
|
expirationTimeInDays: number;
|
|
xpRewardBrackets: RewardBracket[];
|
|
};
|
|
};
|
|
}
|
|
|
|
type StringNumber = `${number}`;
|
|
|
|
interface PersonalBest {
|
|
acc: number;
|
|
consistency?: number;
|
|
difficulty: SharedTypes.Config.Difficulty;
|
|
lazyMode?: boolean;
|
|
language: string;
|
|
punctuation?: boolean;
|
|
numbers?: boolean;
|
|
raw: number;
|
|
wpm: number;
|
|
timestamp: number;
|
|
}
|
|
|
|
interface PersonalBests {
|
|
time: Record<StringNumber, PersonalBest[]>;
|
|
words: Record<StringNumber, PersonalBest[]>;
|
|
quote: Record<StringNumber, PersonalBest[]>;
|
|
custom: Partial<Record<"custom", PersonalBest[]>>;
|
|
zen: Partial<Record<"zen", PersonalBest[]>>;
|
|
}
|
|
|
|
interface IncompleteTest {
|
|
acc: number;
|
|
seconds: number;
|
|
}
|
|
|
|
interface ChartData {
|
|
wpm: number[];
|
|
raw: number[];
|
|
err: number[];
|
|
}
|
|
|
|
interface KeyStats {
|
|
average: number;
|
|
sd: number;
|
|
}
|
|
|
|
interface Result<M extends SharedTypes.Config.Mode> {
|
|
_id: string;
|
|
wpm: number;
|
|
rawWpm: number;
|
|
charStats: [number, number, number, number];
|
|
acc: number;
|
|
mode: M;
|
|
mode2: SharedTypes.Config.Mode2<M>;
|
|
quoteLength?: number;
|
|
timestamp: number;
|
|
restartCount: number;
|
|
incompleteTestSeconds: number;
|
|
incompleteTests: IncompleteTest[];
|
|
testDuration: number;
|
|
afkDuration: number;
|
|
tags: string[];
|
|
consistency: number;
|
|
keyConsistency: number;
|
|
chartData: ChartData | "toolong";
|
|
uid: string;
|
|
keySpacingStats?: KeyStats;
|
|
keyDurationStats?: KeyStats;
|
|
isPb: boolean;
|
|
bailedOut: boolean;
|
|
blindMode: boolean;
|
|
lazyMode: boolean;
|
|
difficulty: SharedTypes.Config.Difficulty;
|
|
funbox: string;
|
|
language: string;
|
|
numbers: boolean;
|
|
punctuation: boolean;
|
|
}
|
|
|
|
interface CustomText {
|
|
text: string[];
|
|
isWordRandom: boolean;
|
|
isTimeRandom: boolean;
|
|
word: number;
|
|
time: number;
|
|
delimiter: string;
|
|
textLen?: number;
|
|
}
|
|
|
|
type DBResult<T extends SharedTypes.Config.Mode> = Omit<
|
|
SharedTypes.Result<T>,
|
|
| "bailedOut"
|
|
| "blindMode"
|
|
| "lazyMode"
|
|
| "difficulty"
|
|
| "funbox"
|
|
| "language"
|
|
| "numbers"
|
|
| "punctuation"
|
|
| "restartCount"
|
|
| "incompleteTestSeconds"
|
|
| "afkDuration"
|
|
| "tags"
|
|
| "incompleteTests"
|
|
| "customText"
|
|
| "quoteLength"
|
|
| "isPb"
|
|
> & {
|
|
correctChars?: number; // --------------
|
|
incorrectChars?: number; // legacy results
|
|
// --------------
|
|
name: string;
|
|
// -------------- fields that might be removed to save space
|
|
bailedOut?: boolean;
|
|
blindMode?: boolean;
|
|
lazyMode?: boolean;
|
|
difficulty?: SharedTypes.Config.Difficulty;
|
|
funbox?: string;
|
|
language?: string;
|
|
numbers?: boolean;
|
|
punctuation?: boolean;
|
|
restartCount?: number;
|
|
incompleteTestSeconds?: number;
|
|
afkDuration?: number;
|
|
tags?: string[];
|
|
customText?: CustomText;
|
|
quoteLength?: number;
|
|
isPb?: boolean;
|
|
};
|
|
|
|
interface CompletedEvent extends Result<SharedTypes.Config.Mode> {
|
|
keySpacing: number[] | "toolong";
|
|
keyDuration: number[] | "toolong";
|
|
customText?: CustomText;
|
|
wpmConsistency: number;
|
|
challenge?: string | null;
|
|
keyOverlap: number;
|
|
lastKeyToEnd: number;
|
|
startToFirstKey: number;
|
|
charTotal: number;
|
|
stringified?: string;
|
|
hash?: string;
|
|
}
|
|
|
|
interface ResultFilters {
|
|
_id: string;
|
|
name: string;
|
|
pb: {
|
|
no: boolean;
|
|
yes: boolean;
|
|
};
|
|
difficulty: {
|
|
normal: boolean;
|
|
expert: boolean;
|
|
master: boolean;
|
|
};
|
|
mode: {
|
|
words: boolean;
|
|
time: boolean;
|
|
quote: boolean;
|
|
zen: boolean;
|
|
custom: boolean;
|
|
};
|
|
words: {
|
|
"10": boolean;
|
|
"25": boolean;
|
|
"50": boolean;
|
|
"100": boolean;
|
|
custom: boolean;
|
|
};
|
|
time: {
|
|
"15": boolean;
|
|
"30": boolean;
|
|
"60": boolean;
|
|
"120": boolean;
|
|
custom: boolean;
|
|
};
|
|
quoteLength: {
|
|
short: boolean;
|
|
medium: boolean;
|
|
long: boolean;
|
|
thicc: boolean;
|
|
};
|
|
punctuation: {
|
|
on: boolean;
|
|
off: boolean;
|
|
};
|
|
numbers: {
|
|
on: boolean;
|
|
off: boolean;
|
|
};
|
|
date: {
|
|
last_day: boolean;
|
|
last_week: boolean;
|
|
last_month: boolean;
|
|
last_3months: boolean;
|
|
all: boolean;
|
|
};
|
|
tags: Record<string, boolean>;
|
|
language: Record<string, boolean>;
|
|
funbox: {
|
|
none?: boolean;
|
|
} & Record<string, boolean>;
|
|
}
|
|
|
|
interface PSA {
|
|
_id: string;
|
|
message: string;
|
|
sticky?: boolean;
|
|
level?: number;
|
|
date?: number;
|
|
}
|
|
|
|
interface SpeedHistogram {
|
|
[key: string]: number;
|
|
}
|
|
|
|
interface PublicTypingStats {
|
|
type: string;
|
|
timeTyping: number;
|
|
testsCompleted: number;
|
|
testsStarted: number;
|
|
}
|
|
|
|
interface ApeKey {
|
|
name: string;
|
|
enabled: boolean;
|
|
createdOn: number;
|
|
modifiedOn: number;
|
|
lastUsedOn: number;
|
|
}
|
|
|
|
interface Config {
|
|
theme: string;
|
|
themeLight: string;
|
|
themeDark: string;
|
|
autoSwitchTheme: boolean;
|
|
customTheme: boolean;
|
|
customThemeColors: string[];
|
|
favThemes: string[];
|
|
showKeyTips: boolean;
|
|
showLiveWpm: boolean;
|
|
showTimerProgress: boolean;
|
|
smoothCaret: SharedTypes.Config.SmoothCaret;
|
|
quickRestart: SharedTypes.Config.QuickRestart;
|
|
punctuation: boolean;
|
|
numbers: boolean;
|
|
words: number;
|
|
time: number;
|
|
mode: SharedTypes.Config.Mode;
|
|
quoteLength: SharedTypes.Config.QuoteLength[];
|
|
language: string;
|
|
fontSize: number;
|
|
freedomMode: boolean;
|
|
difficulty: SharedTypes.Config.Difficulty;
|
|
blindMode: boolean;
|
|
quickEnd: boolean;
|
|
caretStyle: SharedTypes.Config.CaretStyle;
|
|
paceCaretStyle: SharedTypes.Config.CaretStyle;
|
|
flipTestColors: boolean;
|
|
layout: string;
|
|
funbox: string;
|
|
confidenceMode: SharedTypes.Config.ConfidenceMode;
|
|
indicateTypos: SharedTypes.Config.IndicateTypos;
|
|
timerStyle: SharedTypes.Config.TimerStyle;
|
|
colorfulMode: boolean;
|
|
randomTheme: SharedTypes.Config.RandomTheme;
|
|
timerColor: SharedTypes.Config.TimerColor;
|
|
timerOpacity: SharedTypes.Config.TimerOpacity;
|
|
stopOnError: SharedTypes.Config.StopOnError;
|
|
showAllLines: boolean;
|
|
keymapMode: SharedTypes.Config.KeymapMode;
|
|
keymapStyle: SharedTypes.Config.KeymapStyle;
|
|
keymapLegendStyle: SharedTypes.Config.KeymapLegendStyle;
|
|
keymapLayout: string;
|
|
keymapShowTopRow: SharedTypes.Config.KeymapShowTopRow;
|
|
fontFamily: string;
|
|
smoothLineScroll: boolean;
|
|
alwaysShowDecimalPlaces: boolean;
|
|
alwaysShowWordsHistory: boolean;
|
|
singleListCommandLine: SharedTypes.Config.SingleListCommandLine;
|
|
capsLockWarning: boolean;
|
|
playSoundOnError: SharedTypes.Config.PlaySoundOnError;
|
|
playSoundOnClick: SharedTypes.Config.PlaySoundOnClick;
|
|
soundVolume: SharedTypes.Config.SoundVolume;
|
|
startGraphsAtZero: boolean;
|
|
showOutOfFocusWarning: boolean;
|
|
paceCaret: SharedTypes.Config.PaceCaret;
|
|
paceCaretCustomSpeed: number;
|
|
repeatedPace: boolean;
|
|
pageWidth: SharedTypes.Config.PageWidth;
|
|
accountChart: SharedTypes.Config.AccountChart;
|
|
minWpm: SharedTypes.Config.MinimumWordsPerMinute;
|
|
minWpmCustomSpeed: number;
|
|
highlightMode: SharedTypes.Config.HighlightMode;
|
|
typingSpeedUnit: SharedTypes.Config.TypingSpeedUnit;
|
|
ads: SharedTypes.Config.Ads;
|
|
hideExtraLetters: boolean;
|
|
strictSpace: boolean;
|
|
minAcc: SharedTypes.Config.MinimumAccuracy;
|
|
minAccCustom: number;
|
|
showLiveAcc: boolean;
|
|
showLiveBurst: boolean;
|
|
monkey: boolean;
|
|
repeatQuotes: SharedTypes.Config.RepeatQuotes;
|
|
oppositeShiftMode: SharedTypes.Config.OppositeShiftMode;
|
|
customBackground: string;
|
|
customBackgroundSize: SharedTypes.Config.CustomBackgroundSize;
|
|
customBackgroundFilter: SharedTypes.Config.CustomBackgroundFilter;
|
|
customLayoutfluid: SharedTypes.Config.CustomLayoutFluid;
|
|
monkeyPowerLevel: SharedTypes.Config.MonkeyPowerLevel;
|
|
minBurst: SharedTypes.Config.MinimumBurst;
|
|
minBurstCustomSpeed: number;
|
|
burstHeatmap: boolean;
|
|
britishEnglish: boolean;
|
|
lazyMode: boolean;
|
|
showAverage: SharedTypes.Config.ShowAverage;
|
|
tapeMode: SharedTypes.Config.TapeMode;
|
|
}
|
|
|
|
type ConfigValue = Config[keyof Config];
|
|
|
|
type ConfigPreset = Partial<Config> & {
|
|
tags?: string[];
|
|
};
|
|
|
|
interface DBConfigPreset {
|
|
_id: string;
|
|
uid: string;
|
|
name: string;
|
|
config: SharedTypes.ConfigPreset;
|
|
}
|
|
|
|
interface LeaderboardEntry {
|
|
_id: string;
|
|
wpm: number;
|
|
acc: number;
|
|
timestamp: number;
|
|
raw: number;
|
|
consistency?: number;
|
|
uid: string;
|
|
name: string;
|
|
discordId?: string;
|
|
discordAvatar?: string;
|
|
rank: number;
|
|
badgeId?: number;
|
|
isPremium?: boolean;
|
|
}
|
|
|
|
type PostResultResponse = {
|
|
isPb: boolean;
|
|
tagPbs: string[];
|
|
insertedId: string;
|
|
dailyLeaderboardRank?: number;
|
|
weeklyXpLeaderboardRank?: number;
|
|
xp: number;
|
|
dailyXpBonus: boolean;
|
|
xpBreakdown: Record<string, number>;
|
|
streak: number;
|
|
};
|
|
|
|
type UserStreak = {
|
|
lastResultTimestamp: number;
|
|
length: number;
|
|
maxLength: number;
|
|
hourOffset?: number;
|
|
};
|
|
|
|
type UserTag = {
|
|
_id: string;
|
|
name: string;
|
|
personalBests: PersonalBests;
|
|
};
|
|
|
|
type UserProfileDetails = {
|
|
bio?: string;
|
|
keyboard?: string;
|
|
socialProfiles: {
|
|
twitter?: string;
|
|
github?: string;
|
|
website?: string;
|
|
};
|
|
};
|
|
|
|
type CustomTheme = {
|
|
_id: string;
|
|
name: string;
|
|
colors: string[];
|
|
};
|
|
|
|
type PremiumInfo = {
|
|
startTimestamp: number;
|
|
expirationTimestamp: number;
|
|
};
|
|
|
|
type UserQuoteRatings = Record<string, Record<string, number>>;
|
|
|
|
type UserLbMemory = Record<string, Record<string, Record<string, number>>>;
|
|
|
|
type UserInventory = {
|
|
badges: Badge[];
|
|
};
|
|
|
|
type Badge = {
|
|
id: number;
|
|
selected?: boolean;
|
|
};
|
|
|
|
type User = {
|
|
name: string;
|
|
email: string;
|
|
uid: string;
|
|
addedAt: number;
|
|
personalBests: PersonalBests;
|
|
lastReultHashes?: string[]; //todo: fix typo (its in the db too)
|
|
completedTests?: number;
|
|
startedTests?: number;
|
|
timeTyping?: number;
|
|
streak?: UserStreak;
|
|
xp?: number;
|
|
discordId?: string;
|
|
discordAvatar?: string;
|
|
tags?: UserTag[];
|
|
profileDetails?: UserProfileDetails;
|
|
customThemes?: CustomTheme[];
|
|
premium?: PremiumInfo;
|
|
isPremium?: boolean;
|
|
quoteRatings?: UserQuoteRatings;
|
|
favoriteQuotes?: Record<string, string[]>;
|
|
lbMemory?: UserLbMemory;
|
|
inventory?: UserInventory;
|
|
banned?: boolean;
|
|
lbOptOut?: boolean;
|
|
verified?: boolean;
|
|
needsToChangeName?: boolean;
|
|
quoteMod?: boolean | string;
|
|
resultFilterPresets?: ResultFilters[];
|
|
};
|
|
|
|
type Reward<T> = {
|
|
type: string;
|
|
item: T;
|
|
};
|
|
|
|
type XpReward = {
|
|
type: "xp";
|
|
item: number;
|
|
} & Reward<number>;
|
|
|
|
type BadgeReward = {
|
|
type: "badge";
|
|
item: SharedTypes.Badge;
|
|
} & Reward<SharedTypes.Badge>;
|
|
|
|
type AllRewards = XpReward | BadgeReward;
|
|
|
|
type MonkeyMail = {
|
|
id: string;
|
|
subject: string;
|
|
body: string;
|
|
timestamp: number;
|
|
read: boolean;
|
|
rewards: AllRewards[];
|
|
};
|
|
|
|
type UserProfile = Pick<
|
|
User,
|
|
| "name"
|
|
| "banned"
|
|
| "addedAt"
|
|
| "discordId"
|
|
| "discordAvatar"
|
|
| "xp"
|
|
| "lbOptOut"
|
|
| "inventory"
|
|
| "uid"
|
|
| "isPremium"
|
|
> & {
|
|
typingStats: {
|
|
completedTests: User["completedTests"];
|
|
startedTests: User["startedTests"];
|
|
timeTyping: User["timeTyping"];
|
|
};
|
|
streak: UserStreak["length"];
|
|
maxStreak: UserStreak["maxLength"];
|
|
details: UserProfileDetails;
|
|
allTimeLbs: {
|
|
time: Record<string, Record<string, number | null>>;
|
|
};
|
|
personalBests: {
|
|
time: Pick<
|
|
Record<`${number}`, SharedTypes.PersonalBest[]>,
|
|
"15" | "30" | "60" | "120"
|
|
>;
|
|
words: Pick<
|
|
Record<`${number}`, SharedTypes.PersonalBest[]>,
|
|
"10" | "25" | "50" | "100"
|
|
>;
|
|
};
|
|
};
|
|
}
|