fix(codebase audit): preserve cleanup without regressions

Fix codebase audit regressions while preserving UI/UX behavior and adding review-driven hardening.
This commit is contained in:
Tommaso Casaburi
2026-04-24 15:48:07 +07:00
committed by GitHub
parent 5df994b2c7
commit 5dc5408a15
70 changed files with 1478 additions and 518 deletions
+16 -4
View File
@@ -47,29 +47,41 @@ describe('browser hooks', () => {
vi.useRealTimers();
});
it('tracks the window width across resize events', () => {
it('tracks the window width across resize events', async () => {
expect(renderHookValue(() => useWindowWidth())).toBe(1024);
act(() => {
await act(async () => {
window.innerWidth = 480;
window.dispatchEvent(new Event('resize'));
await new Promise<void>((resolve) => window.requestAnimationFrame(() => resolve()));
});
expect(latestValue).toBe(480);
});
it('derives the mobile breakpoint from the current window width', () => {
it('derives the mobile breakpoint from the current window width', async () => {
renderHookValue(() => useIsMobile());
expect(latestValue).toBe(false);
act(() => {
await act(async () => {
window.innerWidth = 639;
window.dispatchEvent(new Event('resize'));
await new Promise<void>((resolve) => window.requestAnimationFrame(() => resolve()));
});
expect(latestValue).toBe(true);
});
it('reads the current width when remounted after a resize with no subscribers', () => {
expect(renderHookValue(() => useWindowWidth())).toBe(1024);
act(() => root.unmount());
window.innerWidth = 480;
root = createRoot(container);
expect(renderHookValue(() => useWindowWidth())).toBe(480);
});
it('updates the current time on the configured interval', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-01-01T00:00:00Z'));
+1 -1
View File
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
import { Comment } from '@bitsocial/bitsocial-react-hooks';
import { flattenCommentsPages } from '@bitsocial/bitsocial-react-hooks/dist/lib/utils';
const useCountLinksInReplies = (comment: Comment, firstXReplies?: number) => {
const useCountLinksInReplies = (comment: Comment | undefined, firstXReplies?: number) => {
let linkCount = 0;
const flattenedReplies = useMemo(() => flattenCommentsPages(comment?.replies), [comment?.replies]);
+4 -1
View File
@@ -18,7 +18,10 @@ export const useCurrentTime = (updateIntervalSeconds: number | false = 60) => {
// Update periodically
const intervalId = setInterval(() => {
setCurrentTime(Date.now() / 1000);
setCurrentTime((previousTime) => {
const nextTime = Date.now() / 1000;
return Math.floor(nextTime) === Math.floor(previousTime) ? previousTime : nextTime;
});
}, updateIntervalSeconds * 1000);
return () => clearInterval(intervalId);
+2 -3
View File
@@ -1,8 +1,7 @@
import useWindowWidth from './use-window-width';
import { useIsMobileBreakpoint } from './use-window-width';
const useIsMobile = () => {
const windowWidth = useWindowWidth();
return windowWidth < 640;
return useIsMobileBreakpoint();
};
export default useIsMobile;
+11 -8
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Comment, usePublishComment } from '@bitsocial/bitsocial-react-hooks';
import { useShallow } from 'zustand/react/shallow';
import usePublishPostStore from '../stores/use-publish-post-store';
import useChallengesStore from '../stores/use-challenges-store';
import usePublishAuthorDomainGuard, { getPublishAuthorDomainErrorMessage } from './use-publish-author-domain-guard';
@@ -9,14 +10,16 @@ type UsePublishPostOptions = {
};
const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
const { author, title, content, link, spoiler, publishCommentOptions } = usePublishPostStore((state) => ({
author: state.author,
title: state.title || undefined,
content: state.content || undefined,
link: state.link || undefined,
spoiler: state.spoiler || false,
publishCommentOptions: state.publishCommentOptions,
}));
const { author, title, content, link, spoiler, publishCommentOptions } = usePublishPostStore(
useShallow((state) => ({
author: state.author,
title: state.title || undefined,
content: state.content || undefined,
link: state.link || undefined,
spoiler: state.spoiler || false,
publishCommentOptions: state.publishCommentOptions,
})),
);
const setPublishPostStore = usePublishPostStore((state) => state.setPublishPostStore);
const resetPublishPostStore = usePublishPostStore((state) => state.resetPublishPostStore);
+10 -7
View File
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Comment, useAccount, usePublishComment } from '@bitsocial/bitsocial-react-hooks';
import { useShallow } from 'zustand/react/shallow';
import { useDirectories } from './use-directories';
import usePublishReplyStore from '../stores/use-publish-reply-store';
import usePostNumberStore, { getScopedNumberToCidMap } from '../stores/use-post-number-store';
@@ -22,13 +23,15 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
const account = useAccount();
const directories = useDirectories();
const { author, content, link, spoiler, publishCommentOptions } = usePublishReplyStore((state) => ({
author: state.author[parentCid],
content: state.content[parentCid] || undefined,
link: state.link[parentCid] || undefined,
spoiler: state.spoiler[parentCid] || false,
publishCommentOptions: state.publishCommentOptions[parentCid],
}));
const { author, content, link, spoiler, publishCommentOptions } = usePublishReplyStore(
useShallow((state) => ({
author: state.author[parentCid],
content: state.content[parentCid] || undefined,
link: state.link[parentCid] || undefined,
spoiler: state.spoiler[parentCid] || false,
publishCommentOptions: state.publishCommentOptions[parentCid],
})),
);
const setPublishReplyStore = usePublishReplyStore((state) => state.setPublishReplyStore);
const resetPublishReplyStore = usePublishReplyStore((state) => state.resetPublishReplyStore);
+1 -1
View File
@@ -47,7 +47,7 @@ const sanitizeSingleFeedLoadingState = (stateString?: string): string | undefine
.replace(/\bloading thread\b/g, 'loading board');
};
const useStateString = (commentOrCommunity: CommentOrCommunity): string | undefined => {
const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): string | undefined => {
const { states: rawStates } = useClientsStates({ comment: commentOrCommunity }) as { states: States };
const debouncedStates = useMemo(() => {
+54 -11
View File
@@ -1,18 +1,61 @@
import { useState, useEffect } from 'react';
import { useSyncExternalStore } from 'react';
const useWindowWidth = () => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const MOBILE_BREAKPOINT_WIDTH = 640;
const SERVER_WIDTH = 1024;
useEffect(() => {
function handleResize() {
setWindowWidth(window.innerWidth);
}
type Listener = () => void;
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const listeners = new Set<Listener>();
let windowWidth = typeof window === 'undefined' ? SERVER_WIDTH : window.innerWidth;
let animationFrameId: number | null = null;
return windowWidth;
const readWindowWidth = () => (typeof window === 'undefined' ? SERVER_WIDTH : window.innerWidth);
const emitIfChanged = () => {
const nextWindowWidth = readWindowWidth();
if (nextWindowWidth === windowWidth) return;
windowWidth = nextWindowWidth;
listeners.forEach((listener) => listener());
};
const handleResize = () => {
if (typeof window === 'undefined' || animationFrameId !== null) return;
animationFrameId = window.requestAnimationFrame(() => {
animationFrameId = null;
emitIfChanged();
});
};
const subscribe = (listener: Listener) => {
listeners.add(listener);
if (typeof window !== 'undefined' && listeners.size === 1) {
windowWidth = readWindowWidth();
window.addEventListener('resize', handleResize, { passive: true });
}
return () => {
listeners.delete(listener);
if (typeof window !== 'undefined' && listeners.size === 0) {
window.removeEventListener('resize', handleResize);
if (animationFrameId !== null) {
window.cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
}
};
};
const getWindowWidthSnapshot = () => windowWidth;
const getServerWindowWidthSnapshot = () => SERVER_WIDTH;
const getIsMobileSnapshot = () => windowWidth < MOBILE_BREAKPOINT_WIDTH;
const getServerIsMobileSnapshot = () => SERVER_WIDTH < MOBILE_BREAKPOINT_WIDTH;
const useWindowWidth = () => useSyncExternalStore(subscribe, getWindowWidthSnapshot, getServerWindowWidthSnapshot);
export const useIsMobileBreakpoint = () => useSyncExternalStore(subscribe, getIsMobileSnapshot, getServerIsMobileSnapshot);
export default useWindowWidth;