diff --git a/src/broadcast/broadcast_manager.ts b/src/broadcast/broadcast_manager.ts
index d022de979..cb36649c7 100644
--- a/src/broadcast/broadcast_manager.ts
+++ b/src/broadcast/broadcast_manager.ts
@@ -1,7 +1,8 @@
import { Preconditions } from "@common/preconditions";
import { ConnectionEvent, ConnectionStatus, DolphinConnection, DolphinMessageType } from "@slippi/slippi-js";
import { EventEmitter } from "events";
-import _ from "lodash";
+import keyBy from "lodash/keyBy";
+import last from "lodash/last";
import type { connection, Message } from "websocket";
import { client as WebSocketClient } from "websocket";
@@ -229,7 +230,7 @@ export class BroadcastManager extends EventEmitter {
switch (message.type) {
case "start-broadcast-resp": {
if (message.recoveryGameCursor !== undefined) {
- const firstIncoming = _.first(this.incomingEvents);
+ const firstIncoming = this.incomingEvents[0];
let firstCursor: number | null | undefined;
if (firstIncoming) {
firstCursor = firstIncoming.cursor;
@@ -246,24 +247,24 @@ export class BroadcastManager extends EventEmitter {
const isNeededByServer = event.cursor > message.recoveryGameCursor;
// Make sure we aren't duplicating anything that is already in the incoming events array
- const isNotIncoming = _.isNil(firstCursor) || event.cursor < firstCursor;
+ const isNotIncoming = firstCursor == null || event.cursor < firstCursor;
return isNeededByServer && isNotIncoming;
}
return false;
});
- this.incomingEvents = _.concat(backedEventsToUse, this.incomingEvents);
+ this.incomingEvents = [...backedEventsToUse, ...this.incomingEvents];
- const newFirstEvent = _.first(this.incomingEvents);
+ const newFirstEvent = this.incomingEvents[0];
if (!newFirstEvent) {
this.emit(BroadcastEvent.LOG, "Missing new first event");
return;
}
const newFirstCursor = newFirstEvent.cursor;
- const firstBackupCursor = (_.first(this.backupEvents) || {}).cursor;
- const lastBackupCursor = (_.last(this.backupEvents) || {}).cursor;
+ const firstBackupCursor = (this.backupEvents[0] || {}).cursor;
+ const lastBackupCursor = (last(this.backupEvents) || {}).cursor;
this.emit(
BroadcastEvent.LOG,
@@ -281,7 +282,7 @@ export class BroadcastManager extends EventEmitter {
// Grab broadcastId we were currently using if the broadcast still exists, would happen
// in the case of a reconnect
if (this.broadcastId) {
- const broadcastsById = _.keyBy(broadcasts, "id");
+ const broadcastsById = keyBy(broadcasts, "id");
const prevBroadcast = broadcastsById[this.broadcastId];
if (prevBroadcast) {
@@ -397,7 +398,7 @@ export class BroadcastManager extends EventEmitter {
return;
}
- while (!_.isEmpty(this.incomingEvents)) {
+ while (this.incomingEvents.length > 0) {
const event = this.incomingEvents.shift();
if (!event) {
this.emit(BroadcastEvent.LOG, "No incoming events");
diff --git a/src/broadcast/spectate_manager.ts b/src/broadcast/spectate_manager.ts
index 51a8730e6..314a1a62e 100644
--- a/src/broadcast/spectate_manager.ts
+++ b/src/broadcast/spectate_manager.ts
@@ -2,7 +2,6 @@ import { Preconditions } from "@common/preconditions";
import { SlpFileWriter, SlpFileWriterEvent } from "@slippi/slippi-js";
import { EventEmitter } from "events";
import * as fs from "fs-extra";
-import _ from "lodash";
import type { connection, Message } from "websocket";
import { client as WebSocketClient } from "websocket";
diff --git a/src/dolphin/instance.ts b/src/dolphin/instance.ts
index f12ac72ad..158a75c2a 100644
--- a/src/dolphin/instance.ts
+++ b/src/dolphin/instance.ts
@@ -5,7 +5,7 @@ import { app } from "electron";
import electronLog from "electron-log";
import { EventEmitter } from "events";
import * as fs from "fs-extra";
-import { debounce } from "lodash";
+import debounce from "lodash/debounce";
import path from "path";
import { fileExists } from "utils/file_exists";
diff --git a/src/dolphin/setup.ts b/src/dolphin/setup.ts
index 0da7ce60a..5b517cf43 100644
--- a/src/dolphin/setup.ts
+++ b/src/dolphin/setup.ts
@@ -1,7 +1,7 @@
import { app, shell } from "electron";
import log from "electron-log";
import * as fs from "fs-extra";
-import { isEqual } from "lodash";
+import isEqual from "lodash/isEqual";
import path from "path";
import { fileExists } from "utils/file_exists";
diff --git a/src/renderer/app/header/header.tsx b/src/renderer/app/header/header.tsx
index 9dab7e869..6d8dc91ea 100644
--- a/src/renderer/app/header/header.tsx
+++ b/src/renderer/app/header/header.tsx
@@ -6,7 +6,7 @@ import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import log from "electron-log";
-import { debounce } from "lodash";
+import debounce from "lodash/debounce";
import React, { useCallback, useMemo } from "react";
import { useDolphinActions } from "@/lib/dolphin/use_dolphin_actions";
diff --git a/src/renderer/components/dual_pane.tsx b/src/renderer/components/dual_pane.tsx
index 1b413eb0e..576d217d5 100644
--- a/src/renderer/components/dual_pane.tsx
+++ b/src/renderer/components/dual_pane.tsx
@@ -1,5 +1,5 @@
import styled from "@emotion/styled";
-import { debounce } from "lodash";
+import debounce from "lodash/debounce";
import React from "react";
import { colors } from "@/styles/colors";
diff --git a/src/renderer/pages/replays/replay_browser/file_list.tsx b/src/renderer/pages/replays/replay_browser/file_list.tsx
index 3bfa80379..509c874a0 100644
--- a/src/renderer/pages/replays/replay_browser/file_list.tsx
+++ b/src/renderer/pages/replays/replay_browser/file_list.tsx
@@ -1,7 +1,7 @@
import DeleteIcon from "@mui/icons-material/Delete";
import FolderIcon from "@mui/icons-material/Folder";
import type { FileResult } from "@replays/types";
-import { debounce } from "lodash";
+import debounce from "lodash/debounce";
import React from "react";
import AutoSizer from "react-virtualized-auto-sizer";
import { FixedSizeList as List } from "react-window";
diff --git a/src/renderer/pages/replays/replay_browser/replay_file/replay_file.container.tsx b/src/renderer/pages/replays/replay_browser/replay_file/replay_file.container.tsx
index a4da9c2c2..a803576ff 100644
--- a/src/renderer/pages/replays/replay_browser/replay_file/replay_file.container.tsx
+++ b/src/renderer/pages/replays/replay_browser/replay_file/replay_file.container.tsx
@@ -10,7 +10,7 @@ import TimerIcon from "@mui/icons-material/Timer";
import TrackChangesIcon from "@mui/icons-material/TrackChanges";
import type { FileResult } from "@replays/types";
import { frameToGameTimer, GameMode, stages as stageUtils } from "@slippi/slippi-js";
-import { groupBy } from "lodash";
+import groupBy from "lodash/groupBy";
import moment from "moment";
import React, { useCallback, useMemo } from "react";
diff --git a/src/renderer/pages/replays/replay_file_stats/game_profile.tsx b/src/renderer/pages/replays/replay_file_stats/game_profile.tsx
index bde354ccd..a78e1d9ee 100644
--- a/src/renderer/pages/replays/replay_file_stats/game_profile.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/game_profile.tsx
@@ -2,7 +2,6 @@ import styled from "@emotion/styled";
import Typography from "@mui/material/Typography";
import type { FileResult } from "@replays/types";
import type { StatsType } from "@slippi/slippi-js";
-import _ from "lodash";
import React from "react";
import { ErrorBoundary } from "@/components/error_boundary";
diff --git a/src/renderer/pages/replays/replay_file_stats/game_profile_header.tsx b/src/renderer/pages/replays/replay_file_stats/game_profile_header.tsx
index 13a63b93e..8d94af8bc 100644
--- a/src/renderer/pages/replays/replay_file_stats/game_profile_header.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/game_profile_header.tsx
@@ -17,7 +17,8 @@ import Tooltip from "@mui/material/Tooltip";
import type { FileResult, PlayerInfo as PlayerInfoType } from "@replays/types";
import type { StadiumStatsType, StatsType } from "@slippi/slippi-js";
import { frameToGameTimer, GameMode, stages as stageUtils } from "@slippi/slippi-js";
-import { get, groupBy } from "lodash";
+import get from "lodash/get";
+import groupBy from "lodash/groupBy";
import moment from "moment";
import React from "react";
diff --git a/src/renderer/pages/replays/replay_file_stats/kill_table.tsx b/src/renderer/pages/replays/replay_file_stats/kill_table.tsx
index ed9896884..94f77bad9 100644
--- a/src/renderer/pages/replays/replay_file_stats/kill_table.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/kill_table.tsx
@@ -7,7 +7,10 @@ import Tooltip from "@mui/material/Tooltip";
import type { FileResult, PlayerInfo } from "@replays/types";
import type { StatsType, StockType } from "@slippi/slippi-js";
import { animations as animationUtils, Frames, moves as moveUtils } from "@slippi/slippi-js";
-import _ from "lodash";
+import get from "lodash/get";
+import groupBy from "lodash/groupBy";
+import keyBy from "lodash/keyBy";
+import last from "lodash/last";
import { convertFrameCountToDurationString } from "@/lib/time";
import { getCharacterIcon } from "@/lib/utils";
@@ -97,13 +100,13 @@ export const KillTable = ({ file, stats, player, opp, onPlay }: KillTableProps)
const renderKilledBy = (stock: StockType) => {
// Here we are going to grab the opponent's punishes and see if one of them was
// responsible for ending this stock, if so show the kill move, otherwise assume SD
- const punishes = _.get(stats, "conversions") || [];
- const punishesByPlayer = _.groupBy(punishes, "playerIndex");
+ const punishes = get(stats, "conversions") || [];
+ const punishesByPlayer = groupBy(punishes, "playerIndex");
const playerPunishes = punishesByPlayer[opp.playerIndex] || [];
// Only get punishes that killed
- const killingPunishes = _.filter(playerPunishes, "didKill");
- const killingPunishesByEndFrame = _.keyBy(killingPunishes, "endFrame");
+ const killingPunishes = playerPunishes.filter((punish) => punish.didKill);
+ const killingPunishesByEndFrame = keyBy(killingPunishes, "endFrame");
const punishThatEndedStock =
stock.endFrame !== null && stock.endFrame !== undefined ? killingPunishesByEndFrame[stock.endFrame] : null;
@@ -112,7 +115,7 @@ export const KillTable = ({ file, stats, player, opp, onPlay }: KillTableProps)
return Self Destruct;
}
- const lastMove = _.last(punishThatEndedStock.moves);
+ const lastMove = last(punishThatEndedStock.moves);
if (!lastMove) {
return Grab Release;
}
@@ -160,8 +163,8 @@ export const KillTable = ({ file, stats, player, opp, onPlay }: KillTableProps)
};
const renderStocksRows = () => {
- const stocks = _.get(stats, "stocks") || [];
- const stocksByOpponent = _.groupBy(stocks, "playerIndex");
+ const stocks = get(stats, "stocks") || [];
+ const stocksByOpponent = groupBy(stocks, "playerIndex");
const opponentStocks = stocksByOpponent[opp.playerIndex] || [];
return opponentStocks.map(generateStockRow);
diff --git a/src/renderer/pages/replays/replay_file_stats/overall_table.tsx b/src/renderer/pages/replays/replay_file_stats/overall_table.tsx
index 16dab96da..bb8d98f12 100644
--- a/src/renderer/pages/replays/replay_file_stats/overall_table.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/overall_table.tsx
@@ -1,6 +1,8 @@
import type { FileResult } from "@replays/types";
import type { RatioType, StatsType } from "@slippi/slippi-js";
-import _ from "lodash";
+import get from "lodash/get";
+import pick from "lodash/pick";
+import toArray from "lodash/toArray";
import { getCharacterIcon } from "@/lib/utils";
@@ -55,8 +57,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
) => {
const key = `standard-field-${header}`;
- const arr = _.get(stats, arrPath) || [];
- const itemsByPlayer = arr; // _.keyBy(arr, "playerIndex");
+ const arr = get(stats, arrPath) || [];
+ const itemsByPlayer = arr; // keyBy(arr, "playerIndex");
if (!arr || arr.length === 0) {
return (
@@ -66,15 +68,11 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
);
}
- const player1Item = arrPathExtension ? _.get(itemsByPlayer[0], arrPathExtension) : itemsByPlayer[0] || {};
- const player2Item = arrPathExtension ? _.get(itemsByPlayer[1], arrPathExtension) : itemsByPlayer[1] || {};
+ const player1Item = arrPathExtension ? get(itemsByPlayer[0], arrPathExtension) : itemsByPlayer[0] || {};
+ const player2Item = arrPathExtension ? get(itemsByPlayer[1], arrPathExtension) : itemsByPlayer[1] || {};
const generateValues = (item: any) => {
if (fieldPaths !== null) {
- return _.chain(item)
- .pick(fieldPaths)
- .toArray()
- .map((v) => (valueMapper ? valueMapper(v) : v))
- .value();
+ return toArray(pick(item, fieldPaths)).map((v) => (valueMapper ? valueMapper(v) : v));
}
if (valueMapper) {
@@ -106,8 +104,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
fieldPath: string,
ratioRenderer: (ratio: RatioType, oppRatio: RatioType) => JSX.Element,
) => {
- const arr = _.get(stats, arrPath) || [];
- const itemsByPlayer = arr; // _.keyBy(arr, "playerIndex");
+ const arr = get(stats, arrPath) || [];
+ const itemsByPlayer = arr; // keyBy(arr, "playerIndex");
const player1Item = itemsByPlayer[0] || {};
const player2Item = itemsByPlayer[1] || {};
@@ -116,8 +114,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
const item = firstPlayer ? player1Item : player2Item;
const oppItem = firstPlayer ? player2Item : player1Item;
- const ratio = _.get(item, fieldPath);
- const oppRatio = _.get(oppItem, fieldPath);
+ const ratio = get(item, fieldPath);
+ const oppRatio = get(oppItem, fieldPath);
return ratioRenderer(ratio, oppRatio);
};
@@ -139,8 +137,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
highlightCondition: (a: number, b: number) => boolean,
) => {
return renderRatioStatField(header, arrPath, fieldPath, (ratio: RatioType, oppRatio: RatioType) => {
- const playerRatio = _.get(ratio, "ratio", null);
- const oppRatioType = _.get(oppRatio, "ratio", null);
+ const playerRatio = get(ratio, "ratio", null);
+ const oppRatioType = get(oppRatio, "ratio", null);
if (playerRatio === null) {
return (
@@ -166,8 +164,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
highlightCondition: (a: number, b: number) => boolean,
) => {
return renderRatioStatField(header, arrPath, fieldPath, (ratio, oppRatio) => {
- const playerRatio = _.get(ratio, "ratio", null);
- const oppRatioType = _.get(oppRatio, "ratio", null);
+ const playerRatio = get(ratio, "ratio", null);
+ const oppRatioType = get(oppRatio, "ratio", null);
if (playerRatio === null || oppRatioType === null) {
return (
@@ -179,8 +177,8 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
const fixedPlayerRatio = playerRatio.toFixed(3);
const fixedOppRatio = oppRatioType.toFixed(3);
- const playerCount = _.get(ratio, "count");
- const playerTotal = _.get(ratio, "total");
+ const playerCount = get(ratio, "count");
+ const playerTotal = get(ratio, "total");
return (
@@ -226,10 +224,10 @@ export const OverallTable = ({ file, stats }: OverallTableProps) => {
highlightCondition: (a: number, b: number) => boolean,
) => {
return renderRatioStatField(header, arrPath, fieldPath, (ratio: RatioType, oppRatio: RatioType) => {
- const playerCount = _.get(ratio, "count") || 0;
- const playerRatio = _.get(ratio, "ratio");
+ const playerCount = get(ratio, "count") || 0;
+ const playerRatio = get(ratio, "ratio");
- const oppCount = _.get(oppRatio, "count") || 0;
+ const oppCount = get(oppRatio, "count") || 0;
let secondaryDisplay = null;
if (playerRatio !== null) {
diff --git a/src/renderer/pages/replays/replay_file_stats/punish_table.tsx b/src/renderer/pages/replays/replay_file_stats/punish_table.tsx
index 7d2583b8c..b3c60c933 100644
--- a/src/renderer/pages/replays/replay_file_stats/punish_table.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/punish_table.tsx
@@ -2,7 +2,10 @@ import { css } from "@emotion/react";
import Tooltip from "@mui/material/Tooltip";
import type { FileResult, PlayerInfo } from "@replays/types";
import type { ConversionType, StatsType, StockType } from "@slippi/slippi-js";
-import _ from "lodash";
+import get from "lodash/get";
+import groupBy from "lodash/groupBy";
+import keyBy from "lodash/keyBy";
+import range from "lodash/range";
import { convertFrameCountToDurationString } from "@/lib/time";
import { getCharacterIcon, toOrdinal } from "@/lib/utils";
@@ -99,7 +102,7 @@ export const PunishTable = ({ file, stats, player, opp, onPlay }: PunishTablePro
const totalStocks = player.startStocks;
const currentStocks = stock.count - 1;
- const stockIcons = _.range(1, totalStocks != null ? totalStocks + 1 : 1).map((stockNum) => {
+ const stockIcons = range(1, totalStocks != null ? totalStocks + 1 : 1).map((stockNum) => {
return (
{
const players = file.game.players || [];
- const playersByIndex = _.keyBy(players, "playerIndex");
+ const playersByIndex = keyBy(players, "playerIndex");
return playersByIndex[playerIndex];
};
@@ -177,12 +180,12 @@ export const PunishTable = ({ file, stats, player, opp, onPlay }: PunishTablePro
};
const renderPunishRows = () => {
- const punishes = _.get(stats, "conversions") || [];
- const punishesByPlayer = _.groupBy(punishes, "playerIndex");
+ const punishes = get(stats, "conversions") || [];
+ const punishesByPlayer = groupBy(punishes, "playerIndex");
const playerPunishes = punishesByPlayer[opp.playerIndex] || [];
- const stocks = _.get(stats, "stocks") || [];
- const stocksTakenFromPlayer = _.groupBy(stocks, "playerIndex");
+ const stocks = get(stats, "stocks") || [];
+ const stocksTakenFromPlayer = groupBy(stocks, "playerIndex");
const opponentStocks = stocksTakenFromPlayer[opp.playerIndex] || [];
const elements: JSX.Element[] = [];
@@ -190,8 +193,8 @@ export const PunishTable = ({ file, stats, player, opp, onPlay }: PunishTablePro
const addStockRows = (punish?: ConversionType) => {
const shouldDisplayStockLoss = () => {
// Calculates whether we should display a stock loss row in this position
- const currentStock = _.first(opponentStocks);
- if (!currentStock || currentStock.endFrame === null || currentStock.endFrame === undefined) {
+ const currentStock = opponentStocks[0];
+ if (!currentStock || currentStock.endFrame == null) {
return false;
}
@@ -208,7 +211,7 @@ export const PunishTable = ({ file, stats, player, opp, onPlay }: PunishTablePro
// rendered one after another. but will initialize to true if we are considering
// the very first punish, this is the handle the case where someone SD's on first
// stock
- let shouldAddEmptyState = punish === _.first(playerPunishes);
+ let shouldAddEmptyState = punish === playerPunishes[0];
while (shouldDisplayStockLoss()) {
const stock = opponentStocks.shift();
@@ -231,7 +234,7 @@ export const PunishTable = ({ file, stats, player, opp, onPlay }: PunishTablePro
// Special case handling when a player finishes their opponent without getting hit
// on their last stock. Still want to show an empty state
- const stock = _.first(opponentStocks);
+ const stock = opponentStocks[0];
if (stock && addedStockRow && !punish) {
const emptyPunishes = generateEmptyRow(stock);
elements.push(emptyPunishes);
diff --git a/src/renderer/pages/replays/replay_file_stats/replay_file_stats.tsx b/src/renderer/pages/replays/replay_file_stats/replay_file_stats.tsx
index 3b1baa474..c6fd31f21 100644
--- a/src/renderer/pages/replays/replay_file_stats/replay_file_stats.tsx
+++ b/src/renderer/pages/replays/replay_file_stats/replay_file_stats.tsx
@@ -8,7 +8,6 @@ import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import type { FileResult } from "@replays/types";
import { GameMode } from "@slippi/slippi-js";
-import _ from "lodash";
import { useQuery } from "react-query";
import { BasicFooter } from "@/components/footer/footer";
diff --git a/src/renderer/pages/settings/chat_settings/chat_messages_input.tsx b/src/renderer/pages/settings/chat_settings/chat_messages_input.tsx
index 522629520..86c7432ad 100644
--- a/src/renderer/pages/settings/chat_settings/chat_messages_input.tsx
+++ b/src/renderer/pages/settings/chat_settings/chat_messages_input.tsx
@@ -9,7 +9,8 @@ import type { SelectChangeEvent } from "@mui/material/Select";
import Select from "@mui/material/Select";
import Typography from "@mui/material/Typography";
import log from "electron-log";
-import { capitalize, chain } from "lodash";
+import capitalize from "lodash/capitalize";
+import orderBy from "lodash/orderBy";
import type { MouseEventHandler } from "react";
import React, { useState } from "react";
@@ -165,45 +166,46 @@ const ChatMessageSelector = ({
}));
};
- const isLockedByMessage = chain(availableMessages)
- .keyBy("text")
- .mapValues((am) => {
- return am.isPaid && user.subLevel === "NONE";
- })
- .value();
+ const isPaidMessage = React.useMemo(() => {
+ return availableMessages.reduce((acc, current) => {
+ acc[current.text] = current.isPaid;
+ return acc;
+ }, {} as Record);
+ }, [availableMessages]);
- const items = chain(availableMessages)
- .orderBy(["isPaid", "text"])
- .filter((am) => am.text !== defaultMessage)
- .map((am) =>
+ const items = React.useMemo(() => {
+ const messageItems = orderBy(availableMessages, ["isPaid", "text"])
+ .filter((am) => am.text !== defaultMessage)
+ .map((am) =>
+ genChatMessageItem(
+ `${groupDirection}-${direction}-${am.text}`,
+ am.text,
+ am.isPaid,
+ false,
+ hoverStates[am.text] ?? false,
+ updateHoverState(am.text),
+ user,
+ ),
+ );
+
+ // Add default message to the top of the list
+ messageItems.unshift(
genChatMessageItem(
- `${groupDirection}-${direction}-${am.text}`,
- am.text,
- am.isPaid,
+ `${groupDirection}-${direction}-${defaultMessage}`,
+ defaultMessage,
false,
- hoverStates[am.text] ?? false,
- updateHoverState(am.text),
+ true,
+ hoverStates[defaultMessage],
+ updateHoverState(defaultMessage),
user,
),
- )
- .value();
+ );
- // Add default message to the top of the list
- items.unshift(
- genChatMessageItem(
- `${groupDirection}-${direction}-${defaultMessage}`,
- defaultMessage,
- false,
- true,
- hoverStates[defaultMessage],
- updateHoverState(defaultMessage),
- user,
- ),
- );
+ return messageItems;
+ }, [availableMessages, defaultMessage, direction, groupDirection, hoverStates, user]);
const onChange = (event: SelectChangeEvent) => {
- console.log(`Updating message to ${event.target.value}. isLocked: ${isLockedByMessage[event.target.value]}`);
- if (isLockedByMessage[event.target.value]) {
+ if (isPaidMessage[event.target.value] && user.subLevel === "NONE") {
// Don't allow change to locked item
return;
}
diff --git a/src/renderer/pages/settings/chat_settings/use_chat_messages.ts b/src/renderer/pages/settings/chat_settings/use_chat_messages.ts
index 4038cf9ab..037ffbb0e 100644
--- a/src/renderer/pages/settings/chat_settings/use_chat_messages.ts
+++ b/src/renderer/pages/settings/chat_settings/use_chat_messages.ts
@@ -1,4 +1,5 @@
-import { isEqual, keyBy } from "lodash";
+import isEqual from "lodash/isEqual";
+import keyBy from "lodash/keyBy";
import { useCallback, useEffect, useState } from "react";
import { useToasts } from "@/lib/hooks/use_toasts";
diff --git a/src/replays/file_system_replay_provider/replays.worker.ts b/src/replays/file_system_replay_provider/replays.worker.ts
index b04d3087b..632b7e9fb 100644
--- a/src/replays/file_system_replay_provider/replays.worker.ts
+++ b/src/replays/file_system_replay_provider/replays.worker.ts
@@ -5,7 +5,6 @@
// TODO: Make electron-log work somehow
import type { StadiumStatsType, StatsType } from "@slippi/slippi-js";
import { SlippiGame } from "@slippi/slippi-js";
-import _ from "lodash";
import type { ModuleMethods } from "threads/dist/types/master";
import { Observable, Subject } from "threads/observable";
import { expose } from "threads/worker";
@@ -54,7 +53,7 @@ const methods: WorkerSpec = {
// For a valid SLP game, at the very least we should have valid settings
const game = new SlippiGame(fullPath);
const settings = game.getSettings();
- if (!settings || _.isEmpty(settings.players)) {
+ if (!settings || settings.players.length === 0) {
throw new Error("Game settings could not be properly loaded.");
}