Skip to content

Optimize client performance and stabilize DM pages#272

Open
Atlas-45 wants to merge 21 commits intoCyberAgentHack:mainfrom
Atlas-45:codex/perf-stabilize
Open

Optimize client performance and stabilize DM pages#272
Atlas-45 wants to merge 21 commits intoCyberAgentHack:mainfrom
Atlas-45:codex/perf-stabilize

Conversation

@Atlas-45
Copy link
Copy Markdown

No description provided.

Copilot AI review requested due to automatic review settings March 20, 2026 15:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on improving client-side performance (smaller initial bundles, better production builds, reduced CPU usage) and addressing stability/performance issues on DM-related pages.

Changes:

  • Replaced jQuery-based networking with fetch and introduced a structured HTTPError.
  • Reworked infinite scrolling and pagination to use limit/offset requests and an IntersectionObserver sentinel.
  • Reduced initial bundle cost via React.lazy/dynamic imports (modals, Crok, translator, analyzer, media converters) and enabled production webpack/Babel optimizations.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
application/client/webpack.config.js Switches dev/prod modes, enables production defaults, adds filesystem cache in prod, removes jQuery ProvidePlugin globals.
application/client/src/utils/fetchers.ts Migrates AJAX calls from jQuery to fetch, adds HTTPError and JSON/error parsing helpers.
application/client/src/index.tsx Avoids rendering when #app is missing; render is no longer deferred to window.load.
application/client/src/index.html Uses defer for the main bundle to improve loading behavior.
application/client/src/hooks/use_ws.ts Adds delayMs/enabled options and delayed WebSocket connection creation.
application/client/src/hooks/use_infinite_fetch.ts Implements server-side pagination (limit/offset) and returns hasMore.
application/client/src/containers/UserProfileContainer.tsx Passes hasMore into InfiniteScroll.
application/client/src/containers/TimelineContainer.tsx Passes hasMore into InfiniteScroll.
application/client/src/containers/SearchContainer.tsx Handles empty query path; passes hasMore into InfiniteScroll.
application/client/src/containers/PostContainer.tsx Passes hasMore into InfiniteScroll for comments.
application/client/src/containers/NewPostModalContainer.tsx Lazily loads the modal page and only loads it after first open.
application/client/src/containers/DirectMessageContainer.tsx Uses delayed WS connection and keeps typing/read behaviors.
application/client/src/containers/AuthModalContainer.tsx Lazily loads the modal page and adapts error handling to HTTPError.
application/client/src/containers/AppContainer.tsx Lazy-loads Crok route and handles /me fetch failures cleanly.
application/client/src/components/post/TranslatableText.tsx Dynamically imports translator helper on demand.
application/client/src/components/new_post_modal/NewPostModalPage.tsx Lazy-loads conversion dependencies and improves conversion error handling.
application/client/src/components/foundation/InfiniteScroll.tsx Replaces scroll-event polling with IntersectionObserver sentinel + hasMore.
application/client/src/components/direct_message/DirectMessagePage.tsx Replaces 1ms interval auto-scroll with effect-driven scroll-to-bottom.
application/client/src/components/direct_message/DirectMessageNotificationBadge.tsx Delays unread WS connection.
application/client/src/components/direct_message/DirectMessageListPage.tsx Delays unread WS connection used to trigger reloads.
application/client/src/components/application/SearchPage.tsx Lazily imports sentiment analyzer and tightens memo deps.
application/client/package.json Builds with NODE_ENV=production.
application/client/babel.config.js Preserves ESM modules for tree-shaking; makes React preset development conditional.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +28
const observer = new IntersectionObserver(
(entries) => {
if (entries.some((entry) => entry.isIntersecting)) {
fetchMore();
}
}

prevReachedRef.current = hasReached;
};

// 最初は実行されないので手動で呼び出す
prevReachedRef.current = false;
handler();
},
{
rootMargin: "200px 0px",
},
);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IntersectionObserver is not supported in IE11, but the client build still explicitly targets IE11 (see babel.config.js targets). As written, infinite scrolling will throw at runtime on IE11. Add an IntersectionObserver polyfill (loaded before this code runs) or provide a fallback path (e.g., scroll/resize listener) when window.IntersectionObserver is undefined.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +108
useWs(
`/api/v1/dm/${conversationId}`,
(event: DmUpdateEvent | DmTypingEvent) => {
if (event.type === "dm:conversation:message") {
void loadConversation().then(() => {
if (event.payload.sender.id !== activeUser?.id) {
setIsPeerTyping(false);
if (peerTypingTimeoutRef.current !== null) {
clearTimeout(peerTypingTimeoutRef.current);
}
peerTypingTimeoutRef.current = null;
}
peerTypingTimeoutRef.current = null;
});
void sendRead();
} else if (event.type === "dm:conversation:typing") {
setIsPeerTyping(true);
if (peerTypingTimeoutRef.current !== null) {
clearTimeout(peerTypingTimeoutRef.current);
}
});
void sendRead();
} else if (event.type === "dm:conversation:typing") {
setIsPeerTyping(true);
if (peerTypingTimeoutRef.current !== null) {
clearTimeout(peerTypingTimeoutRef.current);
peerTypingTimeoutRef.current = setTimeout(() => {
setIsPeerTyping(false);
}, TYPING_INDICATOR_DURATION_MS);
}
peerTypingTimeoutRef.current = setTimeout(() => {
setIsPeerTyping(false);
}, TYPING_INDICATOR_DURATION_MS);
}
});
},
{ delayMs: 5000 },
);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useWs is now invoked even when activeUser is null (this component returns DirectMessageGate, but hooks still run). That will still open a WebSocket to an authenticated endpoint and immediately fail/close, and it also risks connecting with an empty conversationId (""). Since useWs now supports { enabled }, gate the connection (e.g., enabled: activeUser != null && conversationId !== "") and consider whether the 5s delayMs should apply only after the connection is actually needed (to avoid missing real-time updates right after navigation).

Copilot uses AI. Check for mistakes.
Comment on lines 52 to 69
export async function fetchBinary(url: string): Promise<ArrayBuffer> {
const result = await $.ajax({
async: false,
dataType: "binary",
const response = await fetch(url, {
credentials: "same-origin",
method: "GET",
responseType: "arraybuffer",
url,
});
return result;

if (!response.ok) {
return parseErrorResponse(response);
}

return response.arrayBuffer();
}

export async function fetchJSON<T>(url: string): Promise<T> {
const result = await $.ajax({
async: false,
dataType: "json",
const response = await fetch(url, {
credentials: "same-origin",
method: "GET",
url,
});
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fetchers now rely on the browser fetch API. If the ie 11 target in babel.config.js is still intended, this will be a runtime break because fetch is not available without a polyfill (and there’s no fetch polyfill dependency/entrypoint in the client). Either add a fetch polyfill early in the bundle (before these functions are used) or update the build targets to reflect that IE11 isn’t supported.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants