From 9e3f601b580e1302bdbceb513259776e8dba6409 Mon Sep 17 00:00:00 2001 From: Miodec Date: Fri, 14 Feb 2025 20:39:57 +0100 Subject: [PATCH] all ts errors --- frontend/src/ts/elements/input-suggestions.ts | 38 +- frontend/src/ts/test/test-logic.ts | 1 + frontend/src/ts/tribe/tribe-carets.ts | 37 +- .../src/ts/tribe/tribe-chart-controller.ts | 37 +- frontend/src/ts/tribe/tribe-chat.ts | 59 +- frontend/src/ts/tribe/tribe-config.ts | 63 +- frontend/src/ts/tribe/tribe-results.ts | 1 + .../src/ts/tribe/tribe-socket/routes/room.ts | 19 +- .../ts/tribe/tribe-socket/routes/system.ts | 10 +- .../tribe-socket/types/tribe-socket.d.ts | 5 +- frontend/src/ts/tribe/tribe-state.ts | 1 + frontend/src/ts/tribe/tribe-stats.ts | 1 + frontend/src/ts/tribe/tribe-user-list.ts | 7 +- frontend/src/ts/tribe/tribe.ts | 116 +- frontend/src/ts/tribe/types.ts | 134 ++ frontend/src/ts/types/tribe.d.ts | 133 -- frontend/src/ts/utils/json-data.ts | 15 + frontend/static/emoji/_list.json | 1129 +---------------- 18 files changed, 389 insertions(+), 1417 deletions(-) create mode 100644 frontend/src/ts/tribe/types.ts delete mode 100644 frontend/src/ts/types/tribe.d.ts diff --git a/frontend/src/ts/elements/input-suggestions.ts b/frontend/src/ts/elements/input-suggestions.ts index 651e1477e..7d4575ced 100644 --- a/frontend/src/ts/elements/input-suggestions.ts +++ b/frontend/src/ts/elements/input-suggestions.ts @@ -1,3 +1,5 @@ +import * as TribeTypes from "../tribe/types"; + export class InputSuggestions { private inputElement: JQuery; private suggestionsElement: JQuery | undefined; @@ -36,7 +38,7 @@ export class InputSuggestions { this.inputElement.on("input", () => { const inputVal = this.inputElement.val() as string; const split = inputVal.split(" "); - const last = split[split.length - 1]; + const last = split[split.length - 1] as string; const search = last.slice(this.prefix.length); if ( @@ -91,9 +93,9 @@ export class InputSuggestions { const suggestionsElement = document.createElement("div"); suggestionsElement.classList.add("inputSuggestions"); if (this.position === "top") { - this.inputElement[0].before(suggestionsElement); + this.inputElement[0]?.before(suggestionsElement); } else { - this.inputElement[0].after(suggestionsElement); + this.inputElement[0]?.after(suggestionsElement); } this.suggestionsElement = $(suggestionsElement); this.selectedIndex = 0; @@ -139,17 +141,19 @@ export class InputSuggestions { } for (const searchString of this.foundKeys) { - const suggestion = this.data[searchString]; + const suggestion = this.data[ + searchString + ] as TribeTypes.InputSuggestionEntry; const el = `
${ - suggestion.imageIcon + suggestion.imageIcon !== undefined ? `
` - : suggestion.faIcon + : suggestion.faIcon !== undefined ? `
` - : suggestion.textIcon + : suggestion.textIcon !== undefined ? `
${suggestion.textIcon}
` : "" } @@ -172,20 +176,20 @@ export class InputSuggestions { if (!this.suggestionsElement) return; if (this.position === "top") { this.suggestionsElement.css({ - left: this.inputElement[0].offsetLeft + "px", - width: this.inputElement[0].offsetWidth + "px", + left: this.inputElement[0]?.offsetLeft + "px", + width: this.inputElement[0]?.offsetWidth + "px", top: - this.inputElement[0].offsetTop - - this.suggestionsElement[0].offsetHeight + + (this.inputElement[0]?.offsetTop ?? 0) - + (this.suggestionsElement[0]?.offsetHeight ?? 0) + "px", }); } else { this.suggestionsElement.css({ - left: this.inputElement[0].offsetLeft + "px", - width: this.inputElement[0].offsetWidth + "px", + left: this.inputElement[0]?.offsetLeft + "px", + width: this.inputElement[0]?.offsetWidth + "px", top: - this.inputElement[0].offsetTop + - this.inputElement[0].offsetHeight + + (this.inputElement[0]?.offsetTop ?? 0) + + (this.inputElement[0]?.offsetHeight ?? 0) + "px", }); } @@ -215,9 +219,9 @@ export class InputSuggestions { applySelection(): void { if (!this.suggestionsElement) return; if (this.selectedIndex === undefined) return; - if (!this.suggestionsElement) return; + if (this.suggestionsElement === undefined) return; const toInsert = this.foundKeys[this.selectedIndex]; - if (!toInsert) return; + if (toInsert === undefined) return; const currentVal = this.inputElement.val() as string; const split = currentVal.split(" "); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index ffad8bb49..9df6d18c2 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -56,6 +56,7 @@ import * as TribeDelta from "../tribe/tribe-delta"; import * as Random from "../utils/random"; import * as TribeState from "../tribe/tribe-state"; import * as Tribe from "../tribe/tribe"; +import * as TribeTypes from "../tribe/types"; export const glarsesMode = false; diff --git a/frontend/src/ts/tribe/tribe-carets.ts b/frontend/src/ts/tribe/tribe-carets.ts index c01f1ca89..668937a70 100644 --- a/frontend/src/ts/tribe/tribe-carets.ts +++ b/frontend/src/ts/tribe/tribe-carets.ts @@ -1,13 +1,15 @@ import * as TestWords from "../test/test-words"; import * as TestUI from "../test/test-ui"; import Config from "../config"; -import { convertRemToPixels } from "../utils/misc"; import * as SlowTimer from "../states/slow-timer"; import { getRoom } from "./tribe-state"; import tribeSocket from "./tribe-socket"; import * as LineJumpEvent from "../observables/line-jump-event"; import * as ThemeColors from "../elements/theme-colors"; import * as ConfigEvent from "../observables/config-event"; +import * as TestState from "../test/test-state"; +import { convertRemToPixels } from "../utils/numbers"; +import * as TribeTypes from "./types"; const carets: { [key: string]: TribeCaret } = {}; @@ -28,7 +30,8 @@ export class TribeCaret { public spawn(): void { if (this.element) { - return this.destroy(); + this.destroy(); + return; } //create element and store const element = document.createElement("div"); @@ -80,7 +83,8 @@ export class TribeCaret { public animate(animationDuration: number): void { if (!this.element) { this.spawn(); - return this.animate(125); + this.animate(125); + return; } // if ($("#paceCaret").hasClass("hidden")) { // $("#paceCaret").removeClass("hidden"); @@ -106,7 +110,7 @@ export class TribeCaret { try { const newIndex = animationWordIndex - - (TestWords.words.currentIndex - TestUI.currentWordElementIndex); + (TestState.activeWordIndex - TestUI.activeWordElementIndex); currentWord = ( document.querySelectorAll("#words .word")[newIndex] ); @@ -129,6 +133,7 @@ export class TribeCaret { currentLetterWidth === undefined || caretWidth === undefined ) { + // eslint-disable-next-line @typescript-eslint/only-throw-error throw ``; } @@ -149,7 +154,7 @@ export class TribeCaret { this.element.addClass("hidden"); } - const currentTop = this.element[0].offsetTop; + const currentTop = this.element[0]?.offsetTop; if (newTop !== undefined) { const smoothlinescroll = $("#words .smoothScroller").height() ?? 0; @@ -230,7 +235,7 @@ export class TribeCaret { } } - async changeColor(color: keyof MonkeyTypes.ThemeColors): Promise { + async changeColor(color: ThemeColors.ColorName): Promise { if (!this.element) return; const colorHex = await ThemeColors.get(color); this.element.css({ @@ -261,7 +266,7 @@ export function init(): void { if (!room) return; for (const socketId of Object.keys(room.users)) { if (socketId === tribeSocket.getId()) continue; - if (room.users[socketId].isTyping !== true) continue; + if (room.users[socketId]?.isTyping !== true) continue; const name = room.users[socketId].name; @@ -273,11 +278,9 @@ export function updateAndAnimate( data: Record ): void { for (const socketId of Object.keys(data)) { + const d = data[socketId] as TribeTypes.UserProgress; if (!carets[socketId]) continue; - carets[socketId].updatePosition( - data[socketId].wordIndex, - data[socketId].letterIndex - ); + carets[socketId].updatePosition(d.wordIndex, d.letterIndex); carets[socketId].animate(getRoom()?.updateRate ?? 500); } } @@ -285,29 +288,31 @@ export function updateAndAnimate( export function destroy(socketId: string): void { if (carets[socketId]) { carets[socketId].destroy(); + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete carets[socketId]; } } export function changeColor( socketId: string, - color: keyof MonkeyTypes.ThemeColors + color: ThemeColors.ColorName ): void { if (carets[socketId]) { - carets[socketId].changeColor(color); + void carets[socketId].changeColor(color); } } export function destroyAll(): void { for (const socketId of Object.keys(carets)) { - carets[socketId].destroy(); + carets[socketId]?.destroy(); + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete carets[socketId]; } } export function lineJump(offset: number, withAnimation: boolean): void { for (const socketId of Object.keys(carets)) { - carets[socketId].lineJump(offset, withAnimation); + carets[socketId]?.lineJump(offset, withAnimation); } } @@ -328,7 +333,7 @@ ConfigEvent.subscribe((key, value, _nosave, previousValue) => { (value === "on" && previousValue === "noNames") ) { for (const socketId of Object.keys(carets)) { - carets[socketId].setNameVisibility(Config.tribeCarets === "on"); + carets[socketId]?.setNameVisibility(Config.tribeCarets === "on"); } } }); diff --git a/frontend/src/ts/tribe/tribe-chart-controller.ts b/frontend/src/ts/tribe/tribe-chart-controller.ts index 045ab98ce..e1cf47659 100644 --- a/frontend/src/ts/tribe/tribe-chart-controller.ts +++ b/frontend/src/ts/tribe/tribe-chart-controller.ts @@ -2,8 +2,9 @@ import { Chart, ChartConfiguration } from "chart.js"; import * as TribeState from "./tribe-state"; import * as ThemeColors from "../elements/theme-colors"; import * as Notifications from "../elements/notifications"; -import { createErrorMessage, smooth } from "../utils/misc"; +import { createErrorMessage } from "../utils/misc"; import tribeSocket from "./tribe-socket"; +import { smooth } from "../utils/arrays"; const charts: Record = {}; @@ -42,12 +43,12 @@ const settings: ChartConfiguration = { pointStyle: "crossRot", pointRadius: function (context): 0 | 2 { const index = context.dataIndex; - const value = context.dataset.data[index] ?? 0; + const value = (context.dataset.data[index] ?? 0) as number; return value <= 0 ? 0 : 2; }, pointHoverRadius: function (context): 0 | 3 { const index = context.dataIndex; - const value = context.dataset.data[index] ?? 0; + const value = (context.dataset.data[index] ?? 0) as number; return value <= 0 ? 0 : 3; }, }, @@ -257,8 +258,9 @@ async function fillData(chart: Chart, userId: string): Promise { const labels: number[] = []; const room = TribeState.getRoom(); if (!room) return; - const result = room.users[userId].result; + const result = room.users[userId]?.result; if (!result) return; + for (let i = 1; i <= result.chartData.wpm.length; i++) { labels.push(i); } @@ -271,20 +273,27 @@ async function fillData(chart: Chart, userId: string): Promise { const smoothedRawData = smooth(result.chartData.raw, 1); chart.data.labels = labels; + //@ts-expect-error chart.data.datasets[0].data = result.chartData.wpm; + //@ts-expect-error chart.data.datasets[1].data = smoothedRawData; + //@ts-expect-error chart.data.datasets[2].data = result.chartData.err; // chart.options.scales["wpm"].ticks.max = Math.round(chartmaxval); // chart.options.scales["raw"].ticks.max = Math.round(chartmaxval); if (userId == tribeSocket.getId()) { + //@ts-expect-error chart.data.datasets[0].borderColor = await ThemeColors.get("main"); // chart.data.datasets[0].backgroundColor = await ThemeColors.get("main"); } else { + //@ts-expect-error + chart.data.datasets[0].borderColor = await ThemeColors.get("text"); // chart.data.datasets[0].backgroundColor = await ThemeColors.get("text"); } + //@ts-expect-error chart.data.datasets[1].borderColor = await ThemeColors.get("sub"); // chart.data.datasets[1].backgroundColor = await ThemeColors.get("sub"); @@ -303,10 +312,7 @@ export async function drawChart(userId: string): Promise { return; } - const chart = new Chart( - element, - $.extend(true, {}, settings) as ChartConfiguration - ); + const chart = new Chart(element, $.extend(true, {}, settings)); await fillData(chart, userId); @@ -329,7 +335,7 @@ export async function drawAllCharts(): Promise { if (!room) return; const list = Object.keys(room.users); for (let i = 0; i < list.length; i++) { - const userId = list[i]; + const userId = list[i] as string; if (!charts[userId]) { await drawChart(userId); } @@ -343,7 +349,7 @@ export async function updateChartMaxValues(): Promise { let maxRaw = 0; for (const userId of Object.keys(room.users)) { - const result = room.users[userId].result; + const result = room.users[userId]?.result; if (!result) continue; const maxUserWpm = Math.max(maxWpm, Math.max(...result.chartData.wpm)); const maxUserRaw = Math.max(maxRaw, Math.max(...result.chartData.raw)); @@ -358,7 +364,7 @@ export async function updateChartMaxValues(): Promise { const list = Object.keys(room.users); for (let i = 0; i < list.length; i++) { - const userId = list[i]; + const userId = list[i] as string; if (charts[userId]) { const scales = charts[userId].options.scales; if (scales?.["wpm"]) { @@ -370,7 +376,7 @@ export async function updateChartMaxValues(): Promise { scales["raw"].min = 0; } - const result = room.users[userId].result; + const result = room.users[userId]?.result; if (result && scales?.["errors"]) { scales["errors"].max = Math.max(...result.chartData.err) + 1; scales["errors"].min = 0; @@ -380,15 +386,16 @@ export async function updateChartMaxValues(): Promise { // Math.round(chartmaxval); // charts[userId].options.scales.yAxes[1].ticks.max = // Math.round(chartmaxval); - await charts[userId].update(); + charts[userId].update(); } } } export function destroyAllCharts(): void { Object.keys(charts).forEach((userId) => { - charts[userId].clear(); - charts[userId].destroy(); + charts[userId]?.clear(); + charts[userId]?.destroy(); + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete charts[userId]; }); } diff --git a/frontend/src/ts/tribe/tribe-chat.ts b/frontend/src/ts/tribe/tribe-chat.ts index 049527556..edde71f2a 100644 --- a/frontend/src/ts/tribe/tribe-chat.ts +++ b/frontend/src/ts/tribe/tribe-chat.ts @@ -4,6 +4,8 @@ import * as Misc from "../utils/misc"; import * as TestUI from "../test/test-ui"; import tribeSocket from "./tribe-socket"; import { InputSuggestions } from "../elements/input-suggestions"; +import { getEmojiList } from "../utils/json-data"; +import * as TribeTypes from "./types"; let lastMessageTimestamp = 0; let shouldScrollChat = true; @@ -63,7 +65,7 @@ export function isAnyChatSuggestionVisible(): boolean { ); } -Misc.getEmojiList().then((emojis) => { +void getEmojiList().then((emojis) => { const dataToSet: Record = {}; for (const emoji of emojis) { if (emoji.type === "emoji") { @@ -112,7 +114,12 @@ export function reset(where: "both" | "lobby" | "result" = "both"): void { export function fill(where: "both" | "lobby" | "result"): void { for (const message of chatHistory) { - displayMessage(message.isSystem, message.socketId, message.message, where); + void displayMessage( + message.isSystem, + message.socketId, + message.message, + where + ); } } @@ -140,8 +147,10 @@ function sendChattingUpdate(bool: boolean): void { // } export function scrollChat(): void { - const chatEl = $(".pageTribe .lobby .chat .messages")[0]; - const chatEl2 = $(".pageTest #result #tribeResultBottom .chat .messages")[0]; + const chatEl = $(".pageTribe .lobby .chat .messages")[0] as HTMLElement; + const chatEl2 = $( + ".pageTest #result #tribeResultBottom .chat .messages" + )[0] as HTMLElement; if (shouldScrollChat) { chatEl.scrollTop = chatEl.scrollHeight; @@ -162,18 +171,23 @@ export function updateIsTyping(): void { const names: string[] = []; for (const userId of Object.keys(room.users)) { - if (room.users[userId].isChatting && userId !== tribeSocket.getId()) { - names.push(room.users[userId].name); + const user = room.users[userId] as TribeTypes.User; + if (user.isChatting && userId !== tribeSocket.getId()) { + names.push(user.name); } } if (names.length > 0) { for (let i = 0; i < names.length; i++) { if (i === 0) { - string += `${Misc.escapeHTML(names[i])}`; + string += `${Misc.escapeHTML(names[i] ?? "")}`; } else if (i === names.length - 1) { - string += ` and ${Misc.escapeHTML(names[i])}`; + string += ` and ${Misc.escapeHTML( + names[i] ?? "" + )}`; } else { - string += `, ${Misc.escapeHTML(names[i])}`; + string += `, ${Misc.escapeHTML( + names[i] ?? "" + )}`; } } if (names.length == 1) { @@ -194,12 +208,12 @@ async function insertImageEmoji(text: string): Promise { let big = ""; if (textSplit.length === 1) big = "big"; for (let i = 0; i < textSplit.length; i++) { - if (/:.+:/g.test(textSplit[i])) { - const emoji = await Misc.getEmojiList(); + if (/:.+:/g.test(textSplit[i] as string)) { + const emoji = await getEmojiList(); const result = emoji.filter( (e) => Misc.escapeHTML(e.from).toLowerCase() == - textSplit[i].replace(/:/g, "").toLowerCase() + textSplit[i]?.replace(/:/g, "").toLowerCase() ); if (result[0] !== undefined) { if (result[0].type === "image") { @@ -230,7 +244,7 @@ export function appendMessage( chatHistory.splice(0, chatHistory.length - 100); } - displayMessage(isSystem, socketId, message); + void displayMessage(isSystem, socketId, message); } export async function displayMessage( @@ -241,9 +255,10 @@ export async function displayMessage( ): Promise { let cls = "message"; let author = ""; - const fromName = socketId - ? TribeState.getRoom()?.users[socketId]?.name - : undefined; + const fromName = + socketId !== undefined + ? TribeState.getRoom()?.users[socketId]?.name + : undefined; if (isSystem) { cls = "systemMessage"; } else { @@ -367,9 +382,9 @@ $(".pageTest #result #tribeResultBottom .chat .input input").on( $(".pageTribe .lobby .chat .messages").on("scroll", (_e) => { const el = $(".pageTribe .lobby .chat .messages")[0]; - const scrollHeight = el.scrollHeight as number; - const scrollTop = el.scrollTop as number; - const height = el.clientHeight as number; + const scrollHeight = el?.scrollHeight as number; + const scrollTop = el?.scrollTop as number; + const height = el?.clientHeight as number; if (height + scrollTop < scrollHeight - 20) { shouldScrollChat = false; } else { @@ -379,9 +394,9 @@ $(".pageTribe .lobby .chat .messages").on("scroll", (_e) => { $(".pageTest #result #tribeResultBottom .chat .messages").on("scroll", (_e) => { const el = $(".pageTest #result #tribeResultBottom .chat .messages")[0]; - const scrollHeight = el.scrollHeight as number; - const scrollTop = el.scrollTop as number; - const height = el.clientHeight as number; + const scrollHeight = el?.scrollHeight as number; + const scrollTop = el?.scrollTop as number; + const height = el?.clientHeight as number; if (height + scrollTop < scrollHeight - 20) { shouldScrollChat = false; } else { diff --git a/frontend/src/ts/tribe/tribe-config.ts b/frontend/src/ts/tribe/tribe-config.ts index 53f34ce14..90d372212 100644 --- a/frontend/src/ts/tribe/tribe-config.ts +++ b/frontend/src/ts/tribe/tribe-config.ts @@ -1,11 +1,17 @@ import Config, * as UpdateConfig from "../config"; import * as Funbox from "../test/funbox/funbox"; // import * as Notifications from "./notifications"; -import * as CustomText from "../test/custom-text"; +// import * as CustomText from "../test/custom-text"; import * as TribeConfigSyncEvent from "../observables/tribe-config-sync-event"; import * as TribeButtons from "./tribe-buttons"; import * as TribeState from "../tribe/tribe-state"; import tribeSocket from "./tribe-socket"; +import * as TribeTypes from "./types"; +import { Difficulty, Mode } from "@monkeytype/contracts/schemas/shared"; +import { + QuoteLength, + StopOnError, +} from "@monkeytype/contracts/schemas/configs"; export function getArray(config: TribeTypes.RoomConfig): string[] { const ret: string[] = []; @@ -42,10 +48,10 @@ export function getArray(config: TribeTypes.RoomConfig): string[] { if (config["difficulty"] !== "normal") ret.push(config["difficulty"]); // if(config['language'] !== "english") ret.push(config["language"]); - if (config["punctuation"] !== false) ret.push("punctuation"); - if (config["numbers"] !== false) ret.push("numbers"); + if (config["punctuation"]) ret.push("punctuation"); + if (config["numbers"]) ret.push("numbers"); if (config["funbox"] !== "none") ret.push(config["funbox"]); - if (config["lazyMode"] !== false) ret.push("lazy mode"); + if (config["lazyMode"]) ret.push("lazy mode"); if (config["stopOnError"] !== "off") { ret.push("stop on " + config["stopOnError"] == "word" ? "word" : "letter"); } @@ -59,42 +65,30 @@ export function getArray(config: TribeTypes.RoomConfig): string[] { } export function apply(config: TribeTypes.RoomConfig): void { - UpdateConfig.setMode(config.mode as MonkeyTypes.Mode, true, true); + UpdateConfig.setMode(config.mode as Mode, true, true); if (config.mode === "time") { UpdateConfig.setTimeConfig(config.mode2 as number, true, true); } else if (config.mode === "words") { UpdateConfig.setWordCount(config.mode2 as number, true, true); } else if (config.mode === "quote") { - UpdateConfig.setQuoteLength( - config.mode2 as MonkeyTypes.QuoteLength, - true, - true, - true - ); + UpdateConfig.setQuoteLength(config.mode2 as QuoteLength, true, true, true); } else if (config.mode === "custom") { - CustomText.setText(config.customText.text, true); - CustomText.setIsWordRandom(config.customText.isWordRandom, true); - CustomText.setIsTimeRandom(config.customText.isTimeRandom, true); - CustomText.setTime(config.customText.time, true); - CustomText.setWord(config.customText.word, true); + //todo fix + // CustomText.setText(config.customText.text, true); + // CustomText.setIsWordRandom(config.customText.isWordRandom, true); + // CustomText.setIsTimeRandom(config.customText.isTimeRandom, true); + // CustomText.setTime(config.customText.time, true); + // CustomText.setWord(config.customText.word, true); } - UpdateConfig.setDifficulty( - config.difficulty as MonkeyTypes.Difficulty, - true, - true - ); + UpdateConfig.setDifficulty(config.difficulty as Difficulty, true, true); UpdateConfig.setLanguage(config.language, true, true); UpdateConfig.setPunctuation(config.punctuation, true, true); UpdateConfig.setNumbers(config.numbers, true, true); Funbox.setFunbox(config.funbox, true); UpdateConfig.setLazyMode(config.lazyMode, true, true); - UpdateConfig.setStopOnError( - config.stopOnError as MonkeyTypes.StopOnError, - true, - true - ); + UpdateConfig.setStopOnError(config.stopOnError as StopOnError, true, true); if (config.minWpm !== "off") { - UpdateConfig.setMinWpmCustomSpeed(config.minWpm as number, true, true); + UpdateConfig.setMinWpmCustomSpeed(config.minWpm, true, true); UpdateConfig.setMinWpm("custom", true, true); } else { UpdateConfig.setMinWpm("off", true, true); @@ -163,13 +157,14 @@ function sync(): void { minAcc: Config.minAcc === "custom" ? Config.minAccCustom : "off", minBurst: Config.minBurst === "fixed" ? Config.minBurstCustomSpeed : "off", - customText: { - text: CustomText.text, - isWordRandom: CustomText.isWordRandom, - isTimeRandom: CustomText.isTimeRandom, - word: CustomText.word, - time: CustomText.time, - }, + //todo fix + // customText: { + // text: CustomText.text, + // isWordRandom: CustomText.isWordRandom, + // isTimeRandom: CustomText.isTimeRandom, + // word: CustomText.word, + // time: CustomText.time, + // }, isInfiniteTest: (Config.mode == "time" || Config.mode == "words") && mode2 == "0", }); diff --git a/frontend/src/ts/tribe/tribe-results.ts b/frontend/src/ts/tribe/tribe-results.ts index c8704f48c..181046dcf 100644 --- a/frontend/src/ts/tribe/tribe-results.ts +++ b/frontend/src/ts/tribe/tribe-results.ts @@ -4,6 +4,7 @@ import * as SlowTimer from "../states/slow-timer"; import tribeSocket from "./tribe-socket"; import { FinalPositions } from "./tribe-socket/routes/room"; import { getOrdinalNumberString } from "@monkeytype/util/numbers"; +import * as TribeTypes from "./types"; const initialised: Record = {}; diff --git a/frontend/src/ts/tribe/tribe-socket/routes/room.ts b/frontend/src/ts/tribe/tribe-socket/routes/room.ts index 13b7855a9..e09a90253 100644 --- a/frontend/src/ts/tribe/tribe-socket/routes/room.ts +++ b/frontend/src/ts/tribe/tribe-socket/routes/room.ts @@ -1,11 +1,17 @@ import { Mode } from "@monkeytype/contracts/schemas/shared"; import Socket from "../socket"; import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; +import * as TribeTypes from "../../types"; + +type GetPublicRoomsResponse = { + status?: string; + rooms?: TribeTypes.Room[]; +}; async function getPublicRooms( _page: number, search: string -): Promise { +): Promise { return new Promise((resolve) => { Socket.emit( "room_get_public_rooms", @@ -13,22 +19,27 @@ async function getPublicRooms( page: 0, search: search, }, - (e: TribeSocket.GetPublicRoomsResponse) => { + (e: GetPublicRoomsResponse) => { resolve(e); } ); }); } +type JoinRoomResponse = { + status?: string; + room?: TribeTypes.Room; +}; + async function join( roomId: string, fromBrowser: boolean -): Promise { +): Promise { return new Promise((resolve) => { Socket.emit( "room_join", { roomId, fromBrowser }, - (res: TribeSocket.JoinRoomResponse) => { + (res: JoinRoomResponse) => { resolve(res); } ); diff --git a/frontend/src/ts/tribe/tribe-socket/routes/system.ts b/frontend/src/ts/tribe/tribe-socket/routes/system.ts index d2670de0a..c21930277 100644 --- a/frontend/src/ts/tribe/tribe-socket/routes/system.ts +++ b/frontend/src/ts/tribe/tribe-socket/routes/system.ts @@ -1,4 +1,10 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ import Socket from "../socket"; +import * as TribeTypes from "../../types"; async function versionCheck( expectedVersion: string @@ -104,7 +110,9 @@ function connect(callback: () => void): void { }); } -function disconnect(callback: (reason: string, details?: any) => void): void { +function disconnect( + callback: (reason: string, details?: unknown) => void +): void { Socket.on("disconnect", callback); } diff --git a/frontend/src/ts/tribe/tribe-socket/types/tribe-socket.d.ts b/frontend/src/ts/tribe/tribe-socket/types/tribe-socket.d.ts index 43cc244fc..dd5bc971d 100644 --- a/frontend/src/ts/tribe/tribe-socket/types/tribe-socket.d.ts +++ b/frontend/src/ts/tribe/tribe-socket/types/tribe-socket.d.ts @@ -3,10 +3,7 @@ declare namespace TribeSocket { status?: string; rooms?: TribeTypes.Room[]; } - interface JoinRoomResponse { - status?: string; - room?: TribeTypes.Room; - } + interface VersionCheckResponse { status: string; version: string; diff --git a/frontend/src/ts/tribe/tribe-state.ts b/frontend/src/ts/tribe/tribe-state.ts index a60b17f05..b8048efcc 100644 --- a/frontend/src/ts/tribe/tribe-state.ts +++ b/frontend/src/ts/tribe/tribe-state.ts @@ -1,4 +1,5 @@ import TribeSocket from "./tribe-socket"; +import * as TribeTypes from "./types"; let state = -1; diff --git a/frontend/src/ts/tribe/tribe-stats.ts b/frontend/src/ts/tribe/tribe-stats.ts index 1589dec49..20afa9548 100644 --- a/frontend/src/ts/tribe/tribe-stats.ts +++ b/frontend/src/ts/tribe/tribe-stats.ts @@ -1,4 +1,5 @@ import tribeSocket from "./tribe-socket"; +import * as TribeTypes from "./types"; export let inQueueNumbers = [0, 0, 0, 0]; diff --git a/frontend/src/ts/tribe/tribe-user-list.ts b/frontend/src/ts/tribe/tribe-user-list.ts index 72dd01fac..f8a8d49bf 100644 --- a/frontend/src/ts/tribe/tribe-user-list.ts +++ b/frontend/src/ts/tribe/tribe-user-list.ts @@ -1,6 +1,7 @@ import * as TribeState from "./tribe-state"; import * as TribeUserSettingsPopup from "../popups/tribe-user-settings-popup"; import tribeSocket from "./tribe-socket"; +import { User } from "./types"; export function reset(page?: string): void { if (page === undefined) { @@ -16,7 +17,7 @@ export function reset(page?: string): void { export function update(page?: string): void { const room = TribeState.getRoom(); if (!room) return; - if (!page) { + if (page === undefined) { update("lobby"); update("result"); return; @@ -28,8 +29,8 @@ export function update(page?: string): void { usersArray.push(room.users[userId]); } const sortedUsers = usersArray.sort( - (a, b) => (b.points ?? 0) - (a.points ?? 0) - ); + (a, b) => (b?.points ?? 0) - (a?.points ?? 0) + ) as User[]; sortedUsers.forEach((user) => { let icons = ""; if (user.isTyping && !user.isFinished) { diff --git a/frontend/src/ts/tribe/tribe.ts b/frontend/src/ts/tribe/tribe.ts index 58278136f..09e0cce73 100644 --- a/frontend/src/ts/tribe/tribe.ts +++ b/frontend/src/ts/tribe/tribe.ts @@ -28,7 +28,9 @@ import * as TestWords from "../test/test-words"; import * as TestStats from "../test/test-stats"; import * as TestInput from "../test/test-input"; import * as TribeCarets from "./tribe-carets"; +import * as TribeTypes from "./types"; import { navigate } from "../controllers/route-controller"; +import { ColorName } from "../elements/theme-colors"; const defaultName = "Guest"; let name = "Guest"; @@ -111,7 +113,7 @@ function updateState(newState: number): void { } else if (state === 21) { TribeResults.hideTimer(); TribeResults.updateTimerText("Time left for everyone to get ready"); - if (TribeState.getAutoReady() === true) { + if (TribeState.getAutoReady()) { TribeSocket.out.room.readyUpdate(); } } else if (state === 22) { @@ -137,7 +139,7 @@ export async function init(): Promise { //todo remove, only for dev const lstribename = window.localStorage.getItem("tribeName"); - if (lstribename) { + if (lstribename !== undefined && lstribename !== null) { name = lstribename; TribeSocket.updateName(lstribename); } @@ -164,17 +166,17 @@ export function joinRoom(roomId: string, fromBrowser = false): void { return; } - TribeSocket.out.room.join(roomId, fromBrowser).then((response) => { + void TribeSocket.out.room.join(roomId, fromBrowser).then((response) => { if (response.room) { TribeState.setRoom(response.room); updateState(response.room.state); TribePageLobby.init(); - TribePages.change("lobby"); + void TribePages.change("lobby"); TribeSound.play("join"); TribeChat.updateSuggestionData(); // history.replaceState(null, "", `/tribe/${roomId}`); } else { - TribePages.change("menu"); + void TribePages.change("menu"); history.replaceState("/tribe", "", "/tribe"); } }); @@ -218,14 +220,14 @@ async function connect(): Promise { UpdateConfig.setTimerStyle("mini", true); TribePageMenu.enableButtons(); updateState(1); - if (autoJoin) { + if (autoJoin !== undefined) { TribePagePreloader.updateText(`Joining room ${autoJoin}`); TribePagePreloader.updateSubtext("Please wait..."); setTimeout(() => { joinRoom(autoJoin as string); }, 500); } else { - TribePages.change("menu"); + void TribePages.change("menu"); } } @@ -236,10 +238,13 @@ function checkIfEveryoneIsReady(): void { if (Object.keys(room.users).length <= 1) return; let everyoneReady = true; Object.keys(room.users).forEach((userId) => { - if (room && (room.users[userId].isLeader || room.users[userId].isAfk)) { + if ( + room !== undefined && + (room.users[userId]?.isLeader || room.users[userId]?.isAfk) + ) { return; } - if (room && !room.users[userId].isReady) { + if (room !== undefined && !room.users[userId]?.isReady) { everyoneReady = false; } }); @@ -253,12 +258,12 @@ function checkIfEveryoneIsReady(): void { } TribeSocket.in.system.connect(() => { - connect(); + void connect(); }); $(".tribechangename").on("click", () => { const name = prompt("Name"); - if (name) { + if (name !== "" && name !== null) { window.localStorage.setItem("tribeName", name); //todo remove, only for dev TribeSocket.out.user.setName(name, true); } @@ -273,6 +278,7 @@ TribeSocket.in.system.disconnect((reason, details) => { const roomId = TribeState.getRoom()?.id; if (!$(".pageTribe").hasClass("active")) { Notifications.add( + //@ts-ignore `Disconnected: ${details?.["description"]} (${reason})`, -1, { @@ -281,14 +287,15 @@ TribeSocket.in.system.disconnect((reason, details) => { ); } TribeState.setRoom(undefined); - TribePages.change("preloader"); + void TribePages.change("preloader"); TribePagePreloader.updateIcon("times"); TribePagePreloader.updateText(`Disconnected`); + //@ts-ignore TribePagePreloader.updateSubtext(`${details?.["description"]} (${reason})`); TribePagePreloader.showReconnectButton(); - reset(); - if (roomId) { + void reset(); + if (roomId !== undefined) { autoJoin = roomId; } }); @@ -301,12 +308,12 @@ TribeSocket.in.system.connectFailed((err) => { customTitle: "Tribe", }); } - TribePages.change("preloader"); + void TribePages.change("preloader"); TribePagePreloader.updateIcon("times"); TribePagePreloader.updateText("Connection failed"); TribePagePreloader.updateSubtext(err.message); TribePagePreloader.showReconnectButton(); - reset(); + void reset(); }); TribeSocket.in.system.connectError((err) => { @@ -317,12 +324,12 @@ TribeSocket.in.system.connectError((err) => { customTitle: "Tribe", }); } - TribePages.change("preloader"); + void TribePages.change("preloader"); TribePagePreloader.updateIcon("times"); TribePagePreloader.updateText(`Connection error`); TribePagePreloader.updateSubtext(err.message); TribePagePreloader.showReconnectButton(); - reset(); + void reset(); }); TribeSocket.in.system.reconnect((attempt) => { @@ -347,7 +354,7 @@ TribeSocket.in.room.joined((data) => { TribeState.setRoom(data.room); updateState(data.room.state); TribePageLobby.init(); - TribePages.change("lobby"); + void TribePages.change("lobby"); TribeSound.play("join"); TribeChat.updateSuggestionData(); // history.replaceState(null, "", `/tribe/${e.room.id}`); @@ -368,6 +375,7 @@ TribeSocket.in.room.playerJoined((data) => { TribeSocket.in.room.playerLeft((data) => { const room = TribeState.getRoom(); if (room?.users) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete room.users[data.userId]; room.size = Object.keys(room.users).length; TribeUserList.update(); @@ -392,8 +400,8 @@ TribeSocket.in.room.left(() => { } TribeCarets.destroyAll(); TribeSound.play("leave"); - TribePages.change("menu").then(() => { - reset(); + void TribePages.change("menu").then(() => { + void reset(); }); TribeChat.updateIsTyping(); name = defaultName; @@ -416,7 +424,7 @@ TribeSocket.in.room.nameChanged((data) => { TribeSocket.in.room.userIsReady((data) => { const room = TribeState.getRoom(); if (!room) return; - room.users[data.userId].isReady = true; + (room.users[data.userId] as TribeTypes.User).isReady = true; TribeUserList.update(); TribeButtons.update(); checkIfEveryoneIsReady(); @@ -425,7 +433,9 @@ TribeSocket.in.room.userIsReady((data) => { TribeSocket.in.room.userAfkUpdate((data) => { const room = TribeState.getRoom(); if (!room) return; - room.users[data.userId].isAfk = data.isAfk; + const user = room.users[data.userId]; + if (!user) return; + user.isAfk = data.isAfk; TribeUserList.update(); TribeButtons.update(); }); @@ -433,12 +443,15 @@ TribeSocket.in.room.userAfkUpdate((data) => { TribeSocket.in.room.leaderChanged((data) => { const room = TribeState.getRoom(); if (!room) return; + const user = room.users[data.userId]; + if (!user) return; for (const userId of Object.keys(room.users)) { - delete room.users[userId].isLeader; + delete room.users[userId]?.isLeader; } - room.users[data.userId].isLeader = true; - room.users[data.userId].isAfk = false; - room.users[data.userId].isReady = false; + + user.isLeader = true; + user.isAfk = false; + user.isReady = false; TribeUserList.update(); TribeButtons.update(); TribePageLobby.updateVisibility(); @@ -448,7 +461,9 @@ TribeSocket.in.room.leaderChanged((data) => { TribeSocket.in.room.chattingChanged((data) => { const room = TribeState.getRoom(); if (!room) return; - room.users[data.userId].isChatting = data.isChatting; + const user = room.users[data.userId]; + if (!user) return; + user.isChatting = data.isChatting; TribeChat.updateIsTyping(); }); @@ -546,14 +561,15 @@ TribeSocket.in.room.usersUpdate((data) => { let isChattingChanged = false; for (const [userId, user] of Object.entries(data)) { + const roomUser = room.users[userId] as TribeTypes.User; if (user.isTyping !== undefined) { - room.users[userId].isTyping = user.isTyping; + roomUser.isTyping = user.isTyping; } - if (user.isAfk !== undefined) room.users[userId].isAfk = user.isAfk; - if (user.isReady !== undefined) room.users[userId].isReady = user.isReady; + if (user.isAfk !== undefined) roomUser.isAfk = user.isAfk; + if (user.isReady !== undefined) roomUser.isReady = user.isReady; if (user.isChatting !== undefined) { isChattingChanged = true; - room.users[userId].isChatting = user.isChatting; + roomUser.isChatting = user.isChatting; } } TribeUserList.update("lobby"); @@ -588,7 +604,7 @@ TribeSocket.in.room.progressUpdate((data) => { if ( TribeState.getState() >= 10 && TribeState.getState() <= 21 && - TestState.isActive === true + TestState.isActive ) { const wpmAndRaw = TestStats.calculateWpmAndRaw(); const acc = Math.floor(TestStats.calculateAccuracy()); @@ -610,7 +626,7 @@ TribeSocket.in.room.progressUpdate((data) => { } const wordsProgress = Math.floor( - (TestWords.words.currentIndex / outof) * 100 + (TestState.activeWordIndex / outof) * 100 ); progress = wordsProgress + globalWordProgress; @@ -625,15 +641,16 @@ TribeSocket.in.room.progressUpdate((data) => { raw: wpmAndRaw.raw, acc, progress, - wordIndex: TestWords.words.currentIndex, + wordIndex: TestState.activeWordIndex, letterIndex: inputLen - 1, - afk: TestInput.afkHistory[TestInput.afkHistory.length - 1], + afk: TestInput.afkHistory[TestInput.afkHistory.length - 1] ?? false, }); } TribeCarets.updateAndAnimate(data.users); for (const [userId, userProgress] of Object.entries(data.users)) { + if (room.users[userId] === undefined) continue; room.users[userId].progress = userProgress; if (userId == TribeSocket.getId()) { TribeDelta.update(); @@ -657,9 +674,11 @@ TribeSocket.in.room.progressUpdate((data) => { TribeSocket.in.room.userResult((data) => { const room = TribeState.getRoom(); if (!room) return; - room.users[data.userId].result = data.result; - room.users[data.userId].isFinished = true; - room.users[data.userId].isTyping = false; + const user = room.users[data.userId]; + if (!user) return; + user.result = data.result; + user.isFinished = true; + user.isTyping = false; const resolve = data.result?.resolve; if ( resolve === undefined || @@ -673,13 +692,13 @@ TribeSocket.in.room.userResult((data) => { let color = undefined; if (resolve?.failed === true) { - color = "colorfulError" as keyof MonkeyTypes.ThemeColors; + color = "colorfulError" as ColorName; } if (color) TribeCarets.changeColor(data.userId, color); TribeBars.fadeUser("test", data.userId, color); TribeBars.fadeUser("tribe", data.userId, color); - if (room.config.isInfiniteTest === false) { + if (!room.config.isInfiniteTest) { TribeResults.fadeUser("result", data.userId); } if (resolve?.afk) { @@ -704,7 +723,7 @@ TribeSocket.in.room.userResult((data) => { await TribeChartController.drawChart(data.userId); } if (TribeState.getState() >= 21) { - TribeChartController.updateChartMaxValues(); + void TribeChartController.updateChartMaxValues(); } }, 250); } @@ -761,14 +780,19 @@ TribeSocket.in.room.finalPositions((data) => { TribeResults.updateMiniCrowns("result", data.miniCrowns); for (const userArray of Object.values(data.positions)) { for (const user of userArray) { - room.users[user.id].points = user.newPointsTotal; + const u = room.users[user.id] as TribeTypes.User; + u.points = user.newPointsTotal; } } TribeUserList.update(); let localGlow = false; - for (const winner of data.positions["1"]) { + const positions = data.positions["1"]; + + if (!positions) return; + + for (const winner of positions) { if (winner.id === TribeSocket.getId()) { localGlow = true; } @@ -786,7 +810,7 @@ TribeSocket.in.room.finalPositions((data) => { TribeResults.showCrown("result", winner.id, isGlowing); } - if (data.positions[1].some((u) => u.id === TribeSocket.getId())) { + if (positions.some((u) => u.id === TribeSocket.getId())) { TribeSound.play("finish_win"); if (localGlow) { TribeSound.play("glow"); @@ -803,7 +827,7 @@ $(`.pageTribe .tribePage.lobby .lobbyButtons .startTestButton, $(".pageTribe .tribePage.preloader .reconnectButton").on("click", () => { TribePagePreloader.hideReconnectButton(); - init(); + void init(); }); window.addEventListener("beforeunload", () => { diff --git a/frontend/src/ts/tribe/types.ts b/frontend/src/ts/tribe/types.ts new file mode 100644 index 000000000..04c6e75cc --- /dev/null +++ b/frontend/src/ts/tribe/types.ts @@ -0,0 +1,134 @@ +import { ChartData } from "@monkeytype/contracts/schemas/results"; + +export type SystemStats = { + pingStart: number; + stats: [ + number, // online users + rooms: { + mm: [number, number, number, number]; + custom: [number, number]; + }, + queueLengths: [number, number, number, number], + version: string + ]; +}; + +export type InputSuggestionEntry = { + display: string; + imageIcon?: string; + faIcon?: string; + textIcon?: string; +}; + +export type Result = { + wpm: number; + raw: number; + acc: number; + consistency: number; + testDuration: number; + charStats: number[]; + chartData: ChartData; + resolve: ResultResolve; +}; + +export type ResultResolve = { + login?: boolean; + saved?: boolean; + failed?: boolean; + afk?: boolean; + repeated?: boolean; + failedReason?: string; + valid?: boolean; + tooShort?: boolean; + saveFailedMessage?: string; + isPb?: boolean; + bailedOut?: boolean; +}; + +export type RoomJoin = { + room: Room; +}; + +export type Room = { + id: string; + state: number; + users: { + [socketId: string]: User; + }; + size: number; + updateRate: number; + isPrivate: boolean; + name: string; + config: RoomConfig; + maxRaw: number; + maxWpm: number; + minRaw: number; + minWpm: number; + seed: number; +}; + +export type RoomConfig = { + mode: string; + mode2: string | number | number[]; + difficulty: string; + language: string; + punctuation: boolean; + numbers: boolean; + funbox: string; + lazyMode: boolean; + stopOnError: string; + minWpm: number | "off"; + minAcc: number | "off"; + minBurst: number | "off"; + //todo fix + // customText: { + // text: string[]; + // isWordRandom: boolean; + // isTimeRandom: boolean; + // time: number; + // word: number; + // }; + isInfiniteTest: boolean; +}; + +export type UserProgressOut = { + wpm: number; + raw: number; + acc: number; + progress: number; + wordIndex: number; + letterIndex: number; + afk: boolean; +}; + +export type UserProgress = { + wpm: number; + raw: number; + acc: number; + progress: number; + wpmProgress: number; + wordIndex: number; + letterIndex: number; + afk: boolean; +}; + +export type User = { + id: string; + isLeader?: boolean; + name: string; + isReady?: boolean; + result?: Result; + progress?: UserProgress; + isFinished?: boolean; + isTyping?: boolean; + isAfk?: boolean; + isChatting?: boolean; + points?: number; +}; + +export type MiniCrowns = { + raw: string[]; + wpm: string[]; + acc: string[]; + consistency: string[]; +}; diff --git a/frontend/src/ts/types/tribe.d.ts b/frontend/src/ts/types/tribe.d.ts deleted file mode 100644 index c69c9c385..000000000 --- a/frontend/src/ts/types/tribe.d.ts +++ /dev/null @@ -1,133 +0,0 @@ -declare namespace TribeTypes { - interface SystemStats { - pingStart: number; - stats: [ - number, // online users - rooms: { - mm: [number, number, number, number]; - custom: [number, number]; - }, - queueLengths: [number, number, number, number], - version: string - ]; - } - - interface InputSuggestionEntry { - display: string; - imageIcon?: string; - faIcon?: string; - textIcon?: string; - } - - interface Result { - wpm: number; - raw: number; - acc: number; - consistency: number; - testDuration: number; - charStats: number[]; - chartData: MonkeyTypes.ChartData; - resolve: ResultResolve; - } - - interface ResultResolve { - login?: boolean; - saved?: boolean; - failed?: boolean; - afk?: boolean; - repeated?: boolean; - failedReason?: string; - valid?: boolean; - tooShort?: boolean; - saveFailedMessage?: string; - isPb?: boolean; - bailedOut?: boolean; - } - - interface RoomJoin { - room: Room; - } - - interface Room { - id: string; - state: number; - users: { - [socketId: string]: User; - }; - size: number; - updateRate: number; - isPrivate: boolean; - name: string; - config: RoomConfig; - maxRaw: number; - maxWpm: number; - minRaw: number; - minWpm: number; - seed: number; - } - - interface RoomConfig { - mode: string; - mode2: string | number | number[]; - difficulty: string; - language: string; - punctuation: boolean; - numbers: boolean; - funbox: string; - lazyMode: boolean; - stopOnError: string; - minWpm: number | "off"; - minAcc: number | "off"; - minBurst: number | "off"; - customText: { - text: string[]; - isWordRandom: boolean; - isTimeRandom: boolean; - time: number; - word: number; - }; - isInfiniteTest: boolean; - } - - interface UserProgressOut { - wpm: number; - raw: number; - acc: number; - progress: number; - wordIndex: number; - letterIndex: number; - afk: boolean; - } - - interface UserProgress { - wpm: number; - raw: number; - acc: number; - progress: number; - wpmProgress: number; - wordIndex: number; - letterIndex: number; - afk: boolean; - } - - interface User { - id: string; - isLeader?: boolean; - name: string; - isReady?: boolean; - result?: Result; - progress?: UserProgress; - isFinished?: boolean; - isTyping?: boolean; - isAfk?: boolean; - isChatting?: boolean; - points?: number; - } - - interface MiniCrowns { - raw: string[]; - wpm: string[]; - acc: string[]; - consistency: string[]; - } -} diff --git a/frontend/src/ts/utils/json-data.ts b/frontend/src/ts/utils/json-data.ts index ef51dce6b..1e891bfdf 100644 --- a/frontend/src/ts/utils/json-data.ts +++ b/frontend/src/ts/utils/json-data.ts @@ -434,3 +434,18 @@ export async function getReleasesFromGitHub(): Promise { "https://api.github.com/repos/monkeytypegame/monkeytype/releases?per_page=5" ); } + +export type Emoji = { + type: "image" | "emoji"; + from: string; + to: string; +}; + +export async function getEmojiList(): Promise { + try { + const data = await cachedFetchJson("/./emoji/_list.json"); + return data; + } catch (e) { + throw new Error("Emoji list JSON fetch failed"); + } +} diff --git a/frontend/static/emoji/_list.json b/frontend/static/emoji/_list.json index c7cd51ceb..0d05844d0 100644 --- a/frontend/static/emoji/_list.json +++ b/frontend/static/emoji/_list.json @@ -1,19 +1,4 @@ [ - { - "type": "image", - "from": "trollge", - "to": "https://cdn.discordapp.com/emojis/715599037864345670.png?v=1" - }, - { - "type": "image", - "from": "troll", - "to": "https://cdn.discordapp.com/emojis/810584184405098517.png?v=1" - }, - { - "type": "image", - "from": "doggeronafriday", - "to": "https://cdn.discordapp.com/emojis/1060243191254761613.png?v=1" - }, { "type": "image", "from": "kek", @@ -39,6 +24,11 @@ "from": "freakouteyes", "to": "https://cdn.discordapp.com/emojis/762468128323141662.gif?v=1" }, + { + "type": "image", + "from": "pepeHands", + "to": "https://cdn.discordapp.com/emojis/715544844256280689.png?v=1" + }, { "type": "image", "from": "sadge", @@ -89,7 +79,7 @@ "from": "kyle", "to": "https://cdn.discordapp.com/emojis/813586236664184852.png?v=1" }, - + { "type": "image", "from": "thonk", @@ -120,1112 +110,7 @@ "from": "ugh", "to": "https://cdn.discordapp.com/emojis/829545048340168757.png?v=1" }, - { - "type": "image", - "from": "5head" , - "to": "https://cdn.discordapp.com/emojis/737085918107795548.png?v=1" - }, - { - "type": "image", - "from": "BIMShock" , - "to": "https://cdn.discordapp.com/emojis/941012355230339132.png?v=1" - }, - { - "type": "image", - "from": "BIMSit" , - "to": "https://cdn.discordapp.com/emojis/942952920788373505.png?v=1" - }, - { - "type": "image", - "from": "BIMStare" , - "to": "https://cdn.discordapp.com/emojis/941008606705381507.png?v=1" - }, - { - "type": "image", - "from": "BS" , - "to": "https://cdn.discordapp.com/emojis/785183396161781810.png?v=1" - }, - { - "type": "image", - "from": "BapSquish" , - "to": "https://cdn.discordapp.com/emojis/950592110598447114.png?v=1" - }, - { - "type": "image", - "from": "CRY" , - "to": "https://cdn.discordapp.com/emojis/1073058418174197800.png?v=1" - }, - { - "type": "image", - "from": "Despairge" , - "to": "https://cdn.discordapp.com/emojis/1016111265791361074.png?v=1" - }, - { - "type": "image", - "from": "EZ" , - "to": "https://cdn.discordapp.com/emojis/715599037864345670.png?v=1" - }, - { - "type": "image", - "from": "FeelsBadMan" , - "to": "https://cdn.discordapp.com/emojis/725050539712315453.png?v=1" - }, - { - "type": "image", - "from": "LAUGH" , - "to": "https://cdn.discordapp.com/emojis/1073058423488389120.png?v=1" - }, - { - "type": "image", - "from": "NAILS" , - "to": "https://cdn.discordapp.com/emojis/1073057914664800399.png?v=1" - }, - { - "type": "image", - "from": "NO" , - "to": "https://cdn.discordapp.com/emojis/753773334172270662.png?v=1" - }, - { - "type": "image", - "from": "OMG" , - "to": "https://cdn.discordapp.com/emojis/1073058419709313117.png?v=1" - }, - { - "type": "image", - "from": "OOF" , - "to": "https://cdn.discordapp.com/emojis/714261589489877062.png?v=1" - }, - { - "type": "image", - "from": "OkaygeBusiness" , - "to": "https://cdn.discordapp.com/emojis/1045692574586904576.png?v=1" - }, - { - "type": "image", - "from": "PauseChamp" , - "to": "https://cdn.discordapp.com/emojis/799081460169441320.png?v=1" - }, - { - "type": "image", - "from": "PeepoLinus" , - "to": "https://cdn.discordapp.com/emojis/721640114392006676.png?v=1" - }, - { - "type": "image", - "from": "PepeFlushed" , - "to": "https://cdn.discordapp.com/emojis/948404646739861594.png?v=1" - }, - { - "type": "image", - "from": "PepeHands" , - "to": "https://cdn.discordapp.com/emojis/715544844256280689.png?v=1" - }, - { - "type": "image", - "from": "PogChimp" , - "to": "https://cdn.discordapp.com/emojis/715596196470718554.png?v=1" - }, - { - "type": "image", - "from": "SCREAM" , - "to": "https://cdn.discordapp.com/emojis/1073057936793948230.png?v=1" - }, - { - "type": "image", - "from": "Susge" , - "to": "https://cdn.discordapp.com/emojis/938441227802660944.png?v=1" - }, - { - "type": "image", - "from": "TEARS" , - "to": "https://cdn.discordapp.com/emojis/1073058421772931112.png?v=1" - }, - { - "type": "image", - "from": "Trollge" , - "to": "https://cdn.discordapp.com/emojis/998324494030622771.png?v=1" - }, - { - "type": "image", - "from": "accuracy_zero" , - "to": "https://cdn.discordapp.com/emojis/783819412292501504.png?v=1" - }, - { - "type": "image", - "from": "ahh" , - "to": "https://cdn.discordapp.com/emojis/736027796777730048.png?v=1" - }, - { - "type": "image", - "from": "angreydoggo" , - "to": "https://cdn.discordapp.com/emojis/715587667751993444.png?v=1" - }, - { - "type": "image", - "from": "awoooo" , - "to": "https://cdn.discordapp.com/emojis/815618460603383849.png?v=1" - }, - { - "type": "image", - "from": "banana~1" , - "to": "https://cdn.discordapp.com/emojis/868113390063718410.png?v=1" - }, - { - "type": "image", - "from": "bananapepe" , - "to": "https://cdn.discordapp.com/emojis/787529006152155139.png?v=1" - }, - { - "type": "image", - "from": "bbyyoda" , - "to": "https://cdn.discordapp.com/emojis/715591297125318706.png?v=1" - }, - { - "type": "image", - "from": "beantheredonethat" , - "to": "https://cdn.discordapp.com/emojis/781238048369016833.png?v=1" - }, - { - "type": "image", - "from": "bedge" , - "to": "https://cdn.discordapp.com/emojis/998324488359911424.png?v=1" - }, - { - "type": "image", - "from": "beewithagun" , - "to": "https://cdn.discordapp.com/emojis/861665868156108860.png?v=1" - }, - { - "type": "image", - "from": "biblethump" , - "to": "https://cdn.discordapp.com/emojis/715597100410208377.png?v=1" - }, - { - "type": "image", - "from": "blender" , - "to": "https://cdn.discordapp.com/emojis/910006721597034616.png?v=1" - }, - { - "type": "image", - "from": "blurryeyes" , - "to": "https://cdn.discordapp.com/emojis/714259465410445363.png?v=1" - }, - { - "type": "image", - "from": "boi" , - "to": "https://cdn.discordapp.com/emojis/716257280995360780.png?v=1" - }, - { - "type": "image", - "from": "boomer" , - "to": "https://cdn.discordapp.com/emojis/770769761880899594.png?v=1" - }, - { - "type": "image", - "from": "brainlet" , - "to": "https://cdn.discordapp.com/emojis/788215365616271370.png?v=1" - }, - { - "type": "image", - "from": "breadlegs" , - "to": "https://cdn.discordapp.com/emojis/861665854974590996.png?v=1" - }, - { - "type": "image", - "from": "bruhgi" , - "to": "https://cdn.discordapp.com/emojis/1013265363535925383.png?v=1" - }, - { - "type": "image", - "from": "carole" , - "to": "https://cdn.discordapp.com/emojis/715591276547932191.png?v=1" - }, - { - "type": "image", - "from": "catge" , - "to": "https://cdn.discordapp.com/emojis/967843135839420447.png?v=1" - }, - { - "type": "image", - "from": "chadbananabydoggers" , - "to": "https://cdn.discordapp.com/emojis/904289490661691423.png?v=1" - }, - { - "type": "image", - "from": "chuu" , - "to": "https://cdn.discordapp.com/emojis/1028758810023698432.png?v=1" - }, - { - "type": "image", - "from": "clueless" , - "to": "https://cdn.discordapp.com/emojis/998324487160348792.png?v=1" - }, - { - "type": "image", - "from": "corey" , - "to": "https://cdn.discordapp.com/emojis/787177211152302120.png?v=1" - }, - { - "type": "image", - "from": "cororey" , - "to": "https://cdn.discordapp.com/emojis/899482792805679107.png?v=1" - }, - { - "type": "image", - "from": "cowboyape" , - "to": "https://cdn.discordapp.com/emojis/861665880207261726.png?v=1" - }, - { - "type": "image", - "from": "csspika" , - "to": "https://cdn.discordapp.com/emojis/954146930265579650.png?v=1" - }, - { - "type": "image", - "from": "damn" , - "to": "https://cdn.discordapp.com/emojis/715597120324763658.png?v=1" - }, - { - "type": "image", - "from": "deadass" , - "to": "https://cdn.discordapp.com/emojis/1029972103660851220.png?v=1" - }, - { - "type": "image", - "from": "despair" , - "to": "https://cdn.discordapp.com/emojis/1023276827004915783.png?v=1" - }, - { - "type": "image", - "from": "develop" , - "to": "https://cdn.discordapp.com/emojis/735951289464258662.png?v=1" - }, - { - "type": "image", - "from": "developd" , - "to": "https://cdn.discordapp.com/emojis/736242500993286184.png?v=1" - }, - { - "type": "image", - "from": "disgust" , - "to": "https://cdn.discordapp.com/emojis/963573062748499978.png?v=1" - }, - { - "type": "image", - "from": "dogchamp" , - "to": "https://cdn.discordapp.com/emojis/936429905208569946.png?v=1" - }, - { - "type": "image", - "from": "doggeronafriday" , - "to": "https://cdn.discordapp.com/emojis/1060243191254761613.png?v=1" - }, - { - "type": "image", - "from": "dogpeep" , - "to": "https://cdn.discordapp.com/emojis/715598481007444058.png?v=1" - }, - { - "type": "image", - "from": "downvote" , - "to": "https://cdn.discordapp.com/emojis/715596221326164059.png?v=1" - }, - { - "type": "image", - "from": "erm" , - "to": "https://cdn.discordapp.com/emojis/1076654196096507904.png?v=1" - }, - { - "type": "image", - "from": "feelsgoodman" , - "to": "https://cdn.discordapp.com/emojis/715599307419418634.png?v=1" - }, - { - "type": "image", - "from": "feelsstrongman" , - "to": "https://cdn.discordapp.com/emojis/1060242791390793758.png?v=1" - }, - { - "type": "image", - "from": "feelsyepman" , - "to": "https://cdn.discordapp.com/emojis/1060242779541885028.png?v=1" - }, - { - "type": "image", - "from": "flonshed" , - "to": "https://cdn.discordapp.com/emojis/775594420996276262.png?v=1" - }, - { - "type": "image", - "from": "flozshed" , - "to": "https://cdn.discordapp.com/emojis/820150299439136778.png?v=1" - }, - { - "type": "image", - "from": "flugshed" , - "to": "https://cdn.discordapp.com/emojis/773771179898044466.png?v=1" - }, - { - "type": "image", - "from": "flunshed" , - "to": "https://cdn.discordapp.com/emojis/752920742026412133.png?v=1" - }, - { - "type": "image", - "from": "flunshedbean" , - "to": "https://cdn.discordapp.com/emojis/794368098555265085.png?v=1" - }, - { - "type": "image", - "from": "frong" , - "to": "https://cdn.discordapp.com/emojis/843683955228278844.png?v=1" - }, - { - "type": "image", - "from": "gaspm" , - "to": "https://cdn.discordapp.com/emojis/715587682910339123.png?v=1" - }, - { - "type": "image", - "from": "gfruit" , - "to": "https://cdn.discordapp.com/emojis/787177211005501441.png?v=1" - }, - { - "type": "image", - "from": "gigachad" , - "to": "https://cdn.discordapp.com/emojis/963573934542950491.png?v=1" - }, - { - "type": "image", - "from": "gimme" , - "to": "https://cdn.discordapp.com/emojis/778961739328782336.png?v=1" - }, - { - "type": "image", - "from": "grill" , - "to": "https://cdn.discordapp.com/emojis/795711728784506890.png?v=1" - }, - { - "type": "image", - "from": "hehe" , - "to": "https://cdn.discordapp.com/emojis/1029540641522339921.png?v=1" - }, - { - "type": "image", - "from": "heheh" , - "to": "https://cdn.discordapp.com/emojis/748547831714938910.png?v=1" - }, - { - "type": "image", - "from": "hmm" , - "to": "https://cdn.discordapp.com/emojis/715597836825264149.png?v=1" - }, - { - "type": "image", - "from": "hmph" , - "to": "https://cdn.discordapp.com/emojis/736029217380237363.png?v=1" - }, - { - "type": "image", - "from": "holup" , - "to": "https://cdn.discordapp.com/emojis/785675044616798221.png?v=1" - }, - { - "type": "image", - "from": "instalike" , - "to": "https://cdn.discordapp.com/emojis/715598535340589156.png?v=1" - }, - { - "type": "image", - "from": "jasheStopItYou" , - "to": "https://cdn.discordapp.com/emojis/776846606812184576.png?v=1" - }, - { - "type": "image", - "from": "jasheYes" , - "to": "https://cdn.discordapp.com/emojis/776846604165971999.png?v=1" - }, - { - "type": "image", - "from": "jashenana" , - "to": "https://cdn.discordapp.com/emojis/769258751815319552.png?v=1" - }, - { - "type": "image", - "from": "jasheok" , - "to": "https://cdn.discordapp.com/emojis/769262929848827944.png?v=1" - }, - { - "type": "image", - "from": "jeez" , - "to": "https://cdn.discordapp.com/emojis/771858310008668170.png?v=1" - }, - { - "type": "image", - "from": "jefe" , - "to": "https://cdn.discordapp.com/emojis/850792378531971072.png?v=1" - }, - { - "type": "image", - "from": "jerry" , - "to": "https://cdn.discordapp.com/emojis/715597836825264149.png?v=1" - }, - { - "type": "image", - "from": "judgingmonkey" , - "to": "https://cdn.discordapp.com/emojis/715597836825264149.png?v=1" - }, - { - "type": "image", - "from": "jyojoh" , - "to": "https://cdn.discordapp.com/emojis/787177210753843203.png?v=1" - }, - { - "type": "image", - "from": "kachow" , - "to": "https://cdn.discordapp.com/emojis/771225981619798017.png?v=1" - }, - { - "type": "image", - "from": "kachow2" , - "to": "https://cdn.discordapp.com/emojis/771226001036279828.png?v=1" - }, - { - "type": "image", - "from": "kappa" , - "to": "https://cdn.discordapp.com/emojis/735594708159561750.png?v=1" - }, - { - "type": "image", - "from": "katsuu" , - "to": "https://cdn.discordapp.com/emojis/820732478448467968.png?v=1" - }, - { - "type": "image", - "from": "kek" , - "to": "https://cdn.discordapp.com/emojis/1012336279301656688.png?v=1" - }, - { - "type": "image", - "from": "kekold" , - "to": "https://cdn.discordapp.com/emojis/769417555642417172.png?v=1" - }, - { - "type": "image", - "from": "kekw" , - "to": "https://cdn.discordapp.com/emojis/715592474126712832.png?v=1" - }, - { - "type": "image", - "from": "kenn" , - "to": "https://cdn.discordapp.com/emojis/787177211194245120.png?v=1" - }, - { - "type": "image", - "from": "kenntall1" , - "to": "https://cdn.discordapp.com/emojis/787138077533274150.png?v=1" - }, - { - "type": "image", - "from": "kenntall2" , - "to": "https://cdn.discordapp.com/emojis/787138061544849468.png?v=1" - }, - { - "type": "image", - "from": "kk21droidLGrofl" , - "to": "https://cdn.discordapp.com/emojis/1029972094852800512.png?v=1" - }, - { - "type": "image", - "from": "kyle" , - "to": "https://cdn.discordapp.com/emojis/813586236664184852.png?v=1" - }, - { - "type": "image", - "from": "Madeg" , - "to": "https://cdn.discordapp.com/emojis/1048029404980330556.png?v=1" - }, - { - "type": "image", - "from": "mayonnaise" , - "to": "https://cdn.discordapp.com/emojis/774030253835419698.png?v=1" - }, - { - "type": "image", - "from": "meeno" , - "to": "https://cdn.discordapp.com/emojis/897327085771685918.png?v=1" - }, - { - "type": "image", - "from": "meeyes" , - "to": "https://cdn.discordapp.com/emojis/897326981425791016.png?v=1" - }, - { - "type": "image", - "from": "mike" , - "to": "https://cdn.discordapp.com/emojis/715598463806734396.png?v=1" - }, - { - "type": "image", - "from": "mio3d" , - "to": "https://cdn.discordapp.com/emojis/769265704956067852.png?v=1" - }, - { - "type": "image", - "from": "miodik" , - "to": "https://cdn.discordapp.com/emojis/787177211165540392.png?v=1" - }, - { - "type": "image", - "from": "mlady" , - "to": "https://cdn.discordapp.com/emojis/998519727582347316.png?v=1" - }, - { - "type": "image", - "from": "mogU" , - "to": "https://cdn.discordapp.com/emojis/736007114916626522.png?v=1" - }, - { - "type": "image", - "from": "monkaS" , - "to": "https://cdn.discordapp.com/emojis/715624865423687691.png?v=1" - }, - { - "type": "image", - "from": "monkeeyes" , - "to": "https://cdn.discordapp.com/emojis/771858268162097163.png?v=1" - }, - { - "type": "image", - "from": "monkeyS" , - "to": "https://cdn.discordapp.com/emojis/736024188757737532.png?v=1" - }, - { - "type": "image", - "from": "monkeyThink" , - "to": "https://cdn.discordapp.com/emojis/736008152805277776.png?v=1" - }, - { - "type": "image", - "from": "monkeyfacepalm" , - "to": "https://cdn.discordapp.com/emojis/716004425663119380.png?v=1" - }, - { - "type": "image", - "from": "monkeystare" , - "to": "https://cdn.discordapp.com/emojis/788506153867804732.png?v=1" - }, - { - "type": "image", - "from": "monkeywat" , - "to": "https://cdn.discordapp.com/emojis/736026618883080223.png?v=1" - }, - { - "type": "image", - "from": "moodge" , - "to": "https://cdn.discordapp.com/emojis/1058080444471578644.png?v=1" - }, - { - "type": "image", - "from": "moondespair" , - "to": "https://cdn.discordapp.com/emojis/998325448482246780.png?v=1" - }, - { - "type": "image", - "from": "mt" , - "to": "https://cdn.discordapp.com/emojis/879153596388753508.png?v=1" - }, - { - "type": "image", - "from": "musk" , - "to": "https://cdn.discordapp.com/emojis/715598502528286822.png?v=1" - }, - { - "type": "image", - "from": "nahhh" , - "to": "https://cdn.discordapp.com/emojis/1047230589138391201.png?v=1" - }, - { - "type": "image", - "from": "notlikethis" , - "to": "https://cdn.discordapp.com/emojis/735213942942728303.png?v=1" - }, - { - "type": "image", - "from": "octaThink" , - "to": "https://cdn.discordapp.com/emojis/956026830995992656.png?v=1" - }, - { - "type": "image", - "from": "octahappy" , - "to": "https://cdn.discordapp.com/emojis/777709729090109461.png?v=1" - }, - { - "type": "image", - "from": "octamad" , - "to": "https://cdn.discordapp.com/emojis/777151573993324544.png?v=1" - }, - { - "type": "image", - "from": "ohgodohfuck" , - "to": "https://cdn.discordapp.com/emojis/799140202290741268.png?v=1" - }, - { - "type": "image", - "from": "okay" , - "to": "https://cdn.discordapp.com/emojis/998324491132350614.png?v=1" - }, - { - "type": "image", - "from": "okayeg" , - "to": "https://cdn.discordapp.com/emojis/1020399639150735420.png?v=1" - }, - { - "type": "image", - "from": "okayegbusiness" , - "to": "https://cdn.discordapp.com/emojis/1048029227288633394.png?v=1" - }, - { - "type": "image", - "from": "okayegflip" , - "to": "https://cdn.discordapp.com/emojis/1048029427663130644.png?v=1" - }, - { - "type": "image", - "from": "okayge" , - "to": "https://cdn.discordapp.com/emojis/1058080414255824906.png?v=1" - }, - { - "type": "image", - "from": "okaythen" , - "to": "https://cdn.discordapp.com/emojis/788508534232973332.png?v=1" - }, - { - "type": "image", - "from": "ooga" , - "to": "https://cdn.discordapp.com/emojis/715597788628385853.png?v=1" - }, - { - "type": "image", - "from": "our" , - "to": "https://cdn.discordapp.com/emojis/841286875493957682.png?v=1" - }, - { - "type": "image", - "from": "pagman" , - "to": "https://cdn.discordapp.com/emojis/998327207736918036.png?v=1" - }, - { - "type": "image", - "from": "payrespect" , - "to": "https://cdn.discordapp.com/emojis/715591216028188672.png?v=1" - }, - { - "type": "image", - "from": "peepoAngry" , - "to": "https://cdn.discordapp.com/emojis/910974794571329536.png?v=1" - }, - { - "type": "image", - "from": "peepog" , - "to": "https://cdn.discordapp.com/emojis/715597612862144554.png?v=1" - }, - { - "type": "image", - "from": "pensivecornbydoggers" , - "to": "https://cdn.discordapp.com/emojis/905315789949796352.png?v=1" - }, - { - "type": "image", - "from": "pensivecowboy" , - "to": "https://cdn.discordapp.com/emojis/861666275143319552.png?v=1" - }, - { - "type": "image", - "from": "pensivepalmtree" , - "to": "https://cdn.discordapp.com/emojis/824709397313880127.png?v=1" - }, - { - "type": "image", - "from": "pepeaa" , - "to": "https://cdn.discordapp.com/emojis/715597139933397032.png?v=1" - }, - { - "type": "image", - "from": "pepepause" , - "to": "https://cdn.discordapp.com/emojis/859859254474965002.png?v=1" - }, - { - "type": "image", - "from": "pepepoint" , - "to": "https://cdn.discordapp.com/emojis/873063068899373136.png?v=1" - }, - { - "type": "image", - "from": "pepeshrimp" , - "to": "https://cdn.discordapp.com/emojis/820149724203712532.png?v=1" - }, - { - "type": "image", - "from": "pepewot" , - "to": "https://cdn.discordapp.com/emojis/788918785104805959.png?v=1" - }, - { - "type": "image", - "from": "pepewut" , - "to": "https://cdn.discordapp.com/emojis/788916796820422707.png?v=1" - }, - { - "type": "image", - "from": "perhaps" , - "to": "https://cdn.discordapp.com/emojis/751172677481529344.png?v=1" - }, - { - "type": "image", - "from": "pikawaa" , - "to": "https://cdn.discordapp.com/emojis/715587695308439662.png?v=1" - }, - { - "type": "image", - "from": "pingRee" , - "to": "https://cdn.discordapp.com/emojis/714263270198083657.png?v=1" - }, - { - "type": "image", - "from": "pogu" , - "to": "https://cdn.discordapp.com/emojis/731211932387901473.png?v=1" - }, - { - "type": "image", - "from": "poinged" , - "to": "https://cdn.discordapp.com/emojis/802000170329702420.png?v=1" - }, - { - "type": "image", - "from": "poopiga" , - "to": "https://cdn.discordapp.com/emojis/1041564897261129758.png?v=1" - }, - { - "type": "image", - "from": "prayge" , - "to": "https://cdn.discordapp.com/emojis/998324485730082896.png?v=1" - }, - { - "type": "image", - "from": "raccoonchef" , - "to": "https://cdn.discordapp.com/emojis/784567505384374293.png?v=1" - }, - { - "type": "image", - "from": "raisin" , - "to": "https://cdn.discordapp.com/emojis/796886412993101824.png?v=1" - }, - { - "type": "image", - "from": "ree" , - "to": "https://cdn.discordapp.com/emojis/715591191168811039.png?v=1" - }, - { - "type": "image", - "from": "ree~1" , - "to": "https://cdn.discordapp.com/emojis/826489853696016415.png?v=1" - }, - { - "type": "image", - "from": "reee" , - "to": "https://cdn.discordapp.com/emojis/887662047402262568.png?v=1" - }, - { - "type": "image", - "from": "residentsleeper" , - "to": "https://cdn.discordapp.com/emojis/737688425183969374.png?v=1" - }, - { - "type": "image", - "from": "retweet" , - "to": "https://cdn.discordapp.com/emojis/778793383950155776.png?v=1" - }, - { - "type": "image", - "from": "romerono" , - "to": "https://cdn.discordapp.com/emojis/715591201751040060.png?v=1" - }, - { - "type": "image", - "from": "rustle" , - "to": "https://cdn.discordapp.com/emojis/736009097065005096.png?v=1" - }, - { - "type": "image", - "from": "sadcat" , - "to": "https://cdn.discordapp.com/emojis/781232467952664622.png?v=1" - }, - { - "type": "image", - "from": "sadeg" , - "to": "https://cdn.discordapp.com/emojis/1048029802986221660.png?v=1" - }, - { - "type": "image", - "from": "sadge" , - "to": "https://cdn.discordapp.com/emojis/1058080458426032128.png?v=1" - }, - { - "type": "image", - "from": "sellout" , - "to": "https://cdn.discordapp.com/emojis/715592442837205074.png?v=1" - }, - { - "type": "image", - "from": "settiz" , - "to": "https://cdn.discordapp.com/emojis/787177211023196191.png?v=1" - }, - { - "type": "image", - "from": "shrimpcowbow" , - "to": "https://cdn.discordapp.com/emojis/861665829958320178.png?v=1" - }, - { - "type": "image", - "from": "shrimpkiller" , - "to": "https://cdn.discordapp.com/emojis/861665818148339782.png?v=1" - }, - { - "type": "image", - "from": "shrimpr" , - "to": "https://cdn.discordapp.com/emojis/884111452103905400.png?v=1" - }, - { - "type": "image", - "from": "shut" , - "to": "https://cdn.discordapp.com/emojis/811825776202219540.png?v=1" - }, - { - "type": "image", - "from": "smithydoom" , - "to": "https://cdn.discordapp.com/emojis/817175315515834438.png?v=1" - }, - { - "type": "image", - "from": "snugasabug" , - "to": "https://cdn.discordapp.com/emojis/1069865993897254912.png?v=1" - }, - { - "type": "image", - "from": "sonic" , - "to": "https://cdn.discordapp.com/emojis/791693961726918666.png?v=1" - }, - { - "type": "image", - "from": "soontm" , - "to": "https://cdn.discordapp.com/emojis/716256410366640179.png?v=1" - }, - { - "type": "image", - "from": "speedy" , - "to": "https://cdn.discordapp.com/emojis/803781281401274388.png?v=1" - }, - { - "type": "image", - "from": "speedybruh" , - "to": "https://cdn.discordapp.com/emojis/769266559528337479.png?v=1" - }, - { - "type": "image", - "from": "sponge~1" , - "to": "https://cdn.discordapp.com/emojis/799869676418629672.png?v=1" - }, - { - "type": "image", - "from": "surprisedpikachu" , - "to": "https://cdn.discordapp.com/emojis/714262876528836699.png?v=1" - }, - { - "type": "image", - "from": "susge" , - "to": "https://cdn.discordapp.com/emojis/1058080428575166508.png?v=1" - }, - { - "type": "image", - "from": "susgeboy" , - "to": "https://cdn.discordapp.com/emojis/1058080485252812892.png?v=1" - }, - { - "type": "image", - "from": "sussyge" , - "to": "https://cdn.discordapp.com/emojis/1058080357422997655.png?v=1" - }, - { - "type": "image", - "from": "swagrid" , - "to": "https://cdn.discordapp.com/emojis/859377219142156308.png?v=1" - }, - { - "type": "image", - "from": "tapshead" , - "to": "https://cdn.discordapp.com/emojis/714262739627016193.png?v=1" - }, - { - "type": "image", - "from": "taran" , - "to": "https://cdn.discordapp.com/emojis/808178324869873725.png?v=1" - }, - { - "type": "image", - "from": "terrified" , - "to": "https://cdn.discordapp.com/emojis/1047230425388564490.png?v=1" - }, - { - "type": "image", - "from": "this" , - "to": "https://cdn.discordapp.com/emojis/741431049245950013.png?v=1" - }, - { - "type": "image", - "from": "thonkeyes" , - "to": "https://cdn.discordapp.com/emojis/715587711196463114.png?v=1" - }, - { - "type": "image", - "from": "tohruthumbsup" , - "to": "https://cdn.discordapp.com/emojis/1023276868251688970.png?v=1" - }, - { - "type": "image", - "from": "towel" , - "to": "https://cdn.discordapp.com/emojis/715599045850169394.png?v=1" - }, - { - "type": "image", - "from": "towel~1" , - "to": "https://cdn.discordapp.com/emojis/787830460301443082.png?v=1" - }, - { - "type": "image", - "from": "tribewhen" , - "to": "https://cdn.discordapp.com/emojis/857752988789243904.png?v=1" - }, - { - "type": "image", - "from": "troll~1" , - "to": "https://cdn.discordapp.com/emojis/810584184405098517.png?v=1" - }, - { - "type": "image", - "from": "troll~2" , - "to": "https://cdn.discordapp.com/emojis/895397724671848528.png?v=1" - }, - { - "type": "image", - "from": "trollchad" , - "to": "https://cdn.discordapp.com/emojis/963573445084463124.png?v=1" - }, - { - "type": "image", - "from": "tyler" , - "to": "https://cdn.discordapp.com/emojis/874304830594744370.png?v=1" - }, - { - "type": "image", - "from": "ugh" , - "to": "https://cdn.discordapp.com/emojis/852113213452845056.png?v=1" - }, - { - "type": "image", - "from": "uhh" , - "to": "https://cdn.discordapp.com/emojis/715597870233026560.png?v=1" - }, - { - "type": "image", - "from": "untroll" , - "to": "https://cdn.discordapp.com/emojis/810584643094839306.png?v=1" - }, - { - "type": "image", - "from": "upvote" , - "to": "https://cdn.discordapp.com/emojis/715591259636367362.png?v=1" - }, - { - "type": "image", - "from": "usuckkenn" , - "to": "https://cdn.discordapp.com/emojis/790060123623391242.png?v=1" - }, - { - "type": "image", - "from": "vastus" , - "to": "https://cdn.discordapp.com/emojis/787177210867744769.png?v=1" - }, - { - "type": "image", - "from": "waah" , - "to": "https://cdn.discordapp.com/emojis/736028608048529459.png?v=1" - }, - { - "type": "image", - "from": "walt" , - "to": "https://cdn.discordapp.com/emojis/963573669626519594.png?v=1" - }, - { - "type": "image", - "from": "wat" , - "to": "https://cdn.discordapp.com/emojis/788508110318338079.png?v=1" - }, - { - "type": "image", - "from": "wearyyy" , - "to": "https://cdn.discordapp.com/emojis/845320687001665606.png?v=1" - }, - { - "type": "image", - "from": "weirdga" , - "to": "https://cdn.discordapp.com/emojis/900764028857630740.png?v=1" - }, - { - "type": "image", - "from": "weirdgate" , - "to": "https://cdn.discordapp.com/emojis/805654301657399296.png?v=1" - }, - { - "type": "image", - "from": "whatthe" , - "to": "https://cdn.discordapp.com/emojis/714263142280069341.png?v=1" - }, - { - "type": "image", - "from": "whew" , - "to": "https://cdn.discordapp.com/emojis/716257493268955137.png?v=1" - }, - { - "type": "image", - "from": "woop" , - "to": "https://cdn.discordapp.com/emojis/887662014627979275.png?v=1" - }, - { - "type": "image", - "from": "wtf" , - "to": "https://cdn.discordapp.com/emojis/861665841741430796.png?v=1" - }, - { - "type": "image", - "from": "youknowhat" , - "to": "https://cdn.discordapp.com/emojis/716256434651791381.png?v=1" - }, - { - "type": "image", - "from": "ze_or" , - "to": "https://cdn.discordapp.com/emojis/815653579813027891.png?v=1" - }, - { - "type": "image", - "from": "zoinked" , - "to": "https://cdn.discordapp.com/emojis/773583103851102268.png?v=1" - }, - { - "type": "image", - "from": "zoomer" , - "to": "https://cdn.discordapp.com/emojis/77076978360385538.png?v=1" - }, - + { "type": "emoji",