diff --git a/src/components/settings-modal/account-settings/account-settings.tsx b/src/components/settings-modal/account-settings/account-settings.tsx
index 8ab2b4f2..e519a01a 100644
--- a/src/components/settings-modal/account-settings/account-settings.tsx
+++ b/src/components/settings-modal/account-settings/account-settings.tsx
@@ -8,20 +8,21 @@ import { useLocation, useNavigate } from 'react-router-dom';
const isAndroid = Capacitor.getPlatform() === 'android';
-const AccountSettings = () => {
+// Inner component keyed by account id so state resets when user switches account
+const AccountSettingsEditor = ({
+ account,
+}: {
+ account?: { id?: string; name?: string; author?: { address?: string; shortAddress?: string }; [key: string]: unknown };
+}) => {
const { t } = useTranslation();
const location = useLocation();
- const account = useAccount();
- const [text, setText] = useState('');
const accountJson = useMemo(
() => stringify({ account: { ...account, plebbit: undefined, karma: undefined, plebbitReactOptions: undefined, unreadNotificationCount: undefined } }),
[account],
);
- useEffect(() => {
- setText(accountJson);
- }, [accountJson]);
+ const [text, setText] = useState(() => accountJson);
const { accounts } = useAccounts();
const switchToNewAccountRef = useRef(false);
@@ -92,7 +93,7 @@ const AccountSettings = () => {
// Create a temporary download link
const link = document.createElement('a');
link.href = fileUrl;
- link.download = `${account.name}.json`;
+ link.download = `${account?.name ?? 'account'}.json`;
// Append the link, trigger the download, then remove the link
document.body.appendChild(link);
@@ -221,7 +222,7 @@ const AccountSettings = () => {
diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx
index 05a7fa4f..249ce9a4 100644
--- a/src/components/topbar/topbar.tsx
+++ b/src/components/topbar/topbar.tsx
@@ -15,7 +15,9 @@ import useTopbarVisibilityStore from '../../stores/use-topbar-visibility-store';
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
import { BOARD_CODE_GROUPS, getAllBoardCodes } from '../../constants/board-codes';
import styles from './topbar.module.css';
-import { capitalize, debounce, lowerCase } from 'lodash';
+import capitalize from 'lodash/capitalize';
+import debounce from 'lodash/debounce';
+import lowerCase from 'lodash/lowerCase';
const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) => void }) => {
const { t } = useTranslation();
@@ -322,7 +324,7 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
prevScrollPosRef.current = currentScrollPos;
}, 50);
- window.addEventListener('scroll', debouncedHandleScroll);
+ window.addEventListener('scroll', debouncedHandleScroll, { passive: true });
return () => window.removeEventListener('scroll', debouncedHandleScroll);
}, []);
diff --git a/src/hooks/use-state-string.ts b/src/hooks/use-state-string.ts
index ebdd7d32..ad6636f6 100644
--- a/src/hooks/use-state-string.ts
+++ b/src/hooks/use-state-string.ts
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useClientsStates, useSubplebbit, useSubplebbitsStates } from '@plebbit/plebbit-react-hooks';
-import { debounce } from 'lodash';
+import debounce from 'lodash/debounce';
interface CommentOrSubplebbit {
state?: string;
diff --git a/src/views/home/home.tsx b/src/views/home/home.tsx
index b1b273f9..0e5adc43 100644
--- a/src/views/home/home.tsx
+++ b/src/views/home/home.tsx
@@ -12,7 +12,7 @@ import useDirectoryModalStore from '../../stores/use-directory-modal-store';
import DisclaimerModal from '../../components/disclaimer-modal';
import DirectoryModal from '../../components/directory-modal';
import { getBoardPath } from '../../lib/utils/route-utils';
-import { lowerCase } from 'lodash';
+import lowerCase from 'lodash/lowerCase';
// https://github.com/bitsocialhq/lists/blob/master/5chan-directories.json
diff --git a/src/views/home/popular-threads-box/popular-threads-box.tsx b/src/views/home/popular-threads-box/popular-threads-box.tsx
index 8cb5932d..0e176a05 100644
--- a/src/views/home/popular-threads-box/popular-threads-box.tsx
+++ b/src/views/home/popular-threads-box/popular-threads-box.tsx
@@ -70,7 +70,7 @@ const PopularThreadsBox = ({ directories, subplebbits }: { directories: Director
const { t } = useTranslation();
const { showWorksafeContentOnly, showNsfwContentOnly } = usePopularThreadsOptionsStore();
- const getFilteredSubplebbits = () => {
+ const filteredSubplebbits = useMemo(() => {
if (showWorksafeContentOnly) {
return subplebbits.filter((sub: Subplebbit) => {
const directoriesEntry = directories.find((ms) => ms?.address === sub?.address);
@@ -84,9 +84,7 @@ const PopularThreadsBox = ({ directories, subplebbits }: { directories: Director
});
}
return subplebbits;
- };
-
- const filteredSubplebbits = useMemo(getFilteredSubplebbits, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, directories]);
+ }, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, directories]);
const { popularPosts } = usePopularPosts(filteredSubplebbits);
const isLoading = popularPosts.length === 0;
diff --git a/src/views/mod-queue/mod-queue.tsx b/src/views/mod-queue/mod-queue.tsx
index 17e2e6b8..2e5c3807 100644
--- a/src/views/mod-queue/mod-queue.tsx
+++ b/src/views/mod-queue/mod-queue.tsx
@@ -20,8 +20,10 @@ import useChallengesStore from '../../stores/use-challenges-store';
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
import Tooltip from '../../components/tooltip';
import useIsMobile from '../../hooks/use-is-mobile';
+import { useCurrentTime } from '../../hooks/use-current-time';
import { Post } from '../post/post';
-import { capitalize, lowerCase } from 'lodash';
+import capitalize from 'lodash/capitalize';
+import lowerCase from 'lodash/lowerCase';
const { addChallenge } = useChallengesStore.getState();
@@ -158,6 +160,7 @@ const ModQueueRow = ({ comment, isOdd = false }: ModQueueRowProps) => {
const { t } = useTranslation();
const { getAlertThresholdSeconds } = useModQueueStore();
const isMobile = useIsMobile();
+ const currentTime = useCurrentTime();
const { editedComment } = useEditedComment({ comment });
const displayComment = editedComment || comment;
@@ -171,7 +174,7 @@ const ModQueueRow = ({ comment, isOdd = false }: ModQueueRowProps) => {
const alreadyApproved = approved === true;
const alreadyRejected = removed === true;
- const timeWaiting = Date.now() / 1000 - timestamp;
+ const timeWaiting = currentTime - timestamp;
const alertThresholdSeconds = getAlertThresholdSeconds();
const isOverThreshold = timeWaiting > alertThresholdSeconds;
@@ -287,6 +290,7 @@ interface ModQueueCardProps {
const ModQueueCard = ({ comment }: ModQueueCardProps) => {
const { t } = useTranslation();
const { getAlertThresholdSeconds } = useModQueueStore();
+ const currentTime = useCurrentTime();
const { editedComment } = useEditedComment({ comment });
const displayComment = editedComment || comment;
@@ -297,7 +301,7 @@ const ModQueueCard = ({ comment }: ModQueueCardProps) => {
const alreadyApproved = approved === true;
const alreadyRejected = removed === true;
- const timeWaiting = Date.now() / 1000 - timestamp;
+ const timeWaiting = currentTime - timestamp;
const alertThresholdSeconds = getAlertThresholdSeconds();
const isOverThreshold = timeWaiting > alertThresholdSeconds;
const isAwaitingApproval = !alreadyApproved && !alreadyRejected;
@@ -441,21 +445,21 @@ const ModQueueBoardFilter = ({ communities }: ModQueueBoardFilterProps) => {
setSelectedBoardFilter(value);
};
- if (!communities || communities.length === 0) {
- return null;
- }
-
- // Default to first board if none selected
- const firstBoardAddress = communities.find((sub) => sub.address)?.address;
+ // Compute derived values before any early return so hooks run unconditionally
+ const firstBoardAddress = communities?.find((sub) => sub.address)?.address;
const currentFilter = selectedBoardFilter || firstBoardAddress || '';
- // Auto-select first board if none is selected
+ // Auto-select first board if none is selected (must run before early return)
useEffect(() => {
if (!selectedBoardFilter && firstBoardAddress) {
setSelectedBoardFilter(firstBoardAddress);
}
}, [selectedBoardFilter, firstBoardAddress, setSelectedBoardFilter]);
+ if (!communities || communities.length === 0) {
+ return null;
+ }
+
return (