목록 로딩 로직 개선

This commit is contained in:
Macbook
2026-06-07 16:57:30 +09:00
parent 59c548cb41
commit 7ec754770d
25 changed files with 1081 additions and 431 deletions
@@ -35,6 +35,7 @@ import '../styles/virtualTradingDashboard.css';
import '../styles/builderPageShell.css';
import '../styles/tradeNotificationList.css';
import TradeNotificationListRow from './tradeNotification/TradeNotificationListRow';
import { VirtualGridScroll, VirtualScroll } from './common/VirtualScroll';
import TradeNotificationGridLayoutPicker from './tradeNotification/TradeNotificationGridLayoutPicker';
import FullscreenChartModal from './tradeNotification/FullscreenChartModal';
import {
@@ -425,6 +426,47 @@ export const TradeNotificationListPage: React.FC<Props> = ({
const isListView = listLayout === 'list';
const gridCols = getTradeNotificationGridPreset(gridPreset).cols;
const renderNotificationRow = useCallback((item: TradeNotificationItem) => (
<TradeNotificationListRow
key={item.id}
item={item}
theme={theme}
layoutMode={listLayout}
itemTag="div"
isSelected={selectedId === item.id}
chartsEnabled={isListView}
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
onSelect={() => handleNotificationSelect(item)}
onDelete={e => void handleDeleteOne(item, e)}
onDetail={() => {
if (!item.isRead) markAsRead(item.id);
openDetail(item, { centered: true });
}}
onGoToChart={() => {
markAsRead(item.id);
setFullscreenItem(item);
}}
onTrade={() => handleTradeFromAlert(item)}
onReport={() => void handleReportFromAlert(item)}
reportLoading={reportLoadingId === item.id}
tickers={tickers}
/>
), [
theme,
listLayout,
isListView,
selectedId,
chartLiveReceiveHighlight,
handleNotificationSelect,
handleDeleteOne,
markAsRead,
openDetail,
handleTradeFromAlert,
handleReportFromAlert,
reportLoadingId,
tickers,
]);
return (
<>
{fullscreenItem && (
@@ -588,41 +630,39 @@ export const TradeNotificationListPage: React.FC<Props> = ({
<div className="tnl-empty">
{normalizedQuery ? `"${query}" 에 해당하는 알림이 없습니다.` : '표시할 알림이 없습니다.'}
</div>
) : isListView ? (
<VirtualScroll
className="tnl-list tnl-list--gallery vl-scroll"
items={sorted}
estimateSize={384}
measureDynamic
overscan={3}
getItemKey={item => item.id}
role="list"
aria-label="매매 시그널 알림 목록"
>
{item => renderNotificationRow(item)}
</VirtualScroll>
) : (
<ul
<VirtualGridScroll
className={[
'tnl-list',
isListView ? 'tnl-list--gallery' : 'tnl-list--grid',
!isListView ? `tnl-grid--cols-${gridCols}` : '',
].filter(Boolean).join(' ')}
style={!isListView ? { gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))` } : undefined}
'tnl-list--grid',
'vl-scroll',
`tnl-grid--cols-${gridCols}`,
].join(' ')}
items={sorted}
columns={gridCols}
estimateRowSize={300}
rowGap={14}
measureDynamic
overscan={2}
getItemKey={item => item.id}
role="list"
aria-label="매매 시그널 알림 그리드"
>
{sorted.map(item => (
<TradeNotificationListRow
key={item.id}
item={item}
theme={theme}
layoutMode={listLayout}
isSelected={selectedId === item.id}
chartsEnabled={isListView}
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
onSelect={() => handleNotificationSelect(item)}
onDelete={e => void handleDeleteOne(item, e)}
onDetail={() => {
if (!item.isRead) markAsRead(item.id);
openDetail(item, { centered: true });
}}
onGoToChart={() => {
markAsRead(item.id);
setFullscreenItem(item);
}}
onTrade={() => handleTradeFromAlert(item)}
onReport={() => void handleReportFromAlert(item)}
reportLoading={reportLoadingId === item.id}
tickers={tickers}
/>
))}
</ul>
{item => renderNotificationRow(item)}
</VirtualGridScroll>
)}
</div>
</div>