Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-04-29 - Optimize ISO 8601 timestamp sorting in React lists
**Learning:** In React list rendering, sorting by `new Date(ISOString).getTime()` causes significant object allocation overhead on re-renders, especially for larger lists. While `localeCompare` avoids `Date` allocation, raw string comparison (`> / <`) is even faster for raw ASCII strings like ISO dates, avoiding locale checking overhead.
**Action:** Use `b.updatedAt > a.updatedAt ? 1 : b.updatedAt < a.updatedAt ? -1 : 0` instead of `new Date()` allocation or `localeCompare` when sorting by ISO 8601 timestamps, and always memoize derived lists with `useMemo` to prevent redundant O(n log n) sorting operations on every re-render.
22 changes: 14 additions & 8 deletions apps/app/src/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5029,10 +5029,13 @@ export function AppProvider({ children }: { children: ReactNode }) {
? { ...c, updatedAt: new Date().toISOString() }
: c,
);
return updated.sort(
(a, b) =>
new Date(b.updatedAt).getTime() -
new Date(a.updatedAt).getTime(),
// ⚡ Bolt: Use raw string comparison for ISO 8601 strings to avoid Date allocation
return updated.sort((a, b) =>
b.updatedAt > a.updatedAt
? 1
: b.updatedAt < a.updatedAt
? -1
: 0,
);
Comment on lines +5033 to 5039

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The sorting logic using nested ternary operators is somewhat difficult to read. While the raw string comparison is efficient, using a more standard conditional structure or a helper function would improve maintainability. Additionally, this exact logic is duplicated later in the same file.

            return updated.sort((a, b) => {
              if (b.updatedAt > a.updatedAt) return 1;
              if (b.updatedAt < a.updatedAt) return -1;
              return 0;
            });

});
},
Expand All @@ -5046,10 +5049,13 @@ export function AppProvider({ children }: { children: ReactNode }) {
if (conv?.id) {
setConversations((prev) => {
const updated = prev.map((c) => (c.id === conv.id ? conv : c));
return updated.sort(
(a, b) =>
new Date(b.updatedAt).getTime() -
new Date(a.updatedAt).getTime(),
// ⚡ Bolt: Use raw string comparison for ISO 8601 strings to avoid Date allocation
return updated.sort((a, b) =>
b.updatedAt > a.updatedAt
? 1
: b.updatedAt < a.updatedAt
? -1
: 0,
);
Comment on lines +5053 to 5059

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This sorting logic is identical to the one used in the proactive-message handler. Consider extracting this into a shared utility function to reduce duplication and ensure consistency across the application.

              return updated.sort((a, b) => {
                if (b.updatedAt > a.updatedAt) return 1;
                if (b.updatedAt < a.updatedAt) return -1;
                return 0;
              });

});
}
Expand Down
13 changes: 7 additions & 6 deletions apps/app/src/components/ConversationsSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Conversations sidebar component — left sidebar with conversation list.
*/

import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useApp } from "../AppContext";

interface ConversationsSidebarProps {
Expand Down Expand Up @@ -54,11 +54,12 @@ export function ConversationsSidebar({
}
}, [editingId]);

const sortedConversations = [...conversations].sort((a, b) => {
const aTime = new Date(a.updatedAt).getTime();
const bTime = new Date(b.updatedAt).getTime();
return bTime - aTime;
});
// ⚡ Bolt: Memoize the sorting and use raw string comparison for ISO 8601 strings to prevent O(n log n) object allocation overhead on re-renders
const sortedConversations = useMemo(() => {
return [...conversations].sort((a, b) =>
b.updatedAt > a.updatedAt ? 1 : b.updatedAt < a.updatedAt ? -1 : 0,
);
}, [conversations]);
Comment on lines +58 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Potential Sorting Order Issue:
The sorting logic in useMemo appears to sort conversations in ascending order by updatedAt (oldest first), but typical UX expects the most recent conversations to appear first. To sort in descending order (newest first), reverse the comparison:

return [...conversations].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));

This ensures the newest conversations are at the top of the sidebar.


const handleDoubleClick = (conv: { id: string; title: string }) => {
setEditingId(conv.id);
Expand Down
Loading