목록 로딩 로직 개선

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
@@ -5,6 +5,7 @@ import { useAuth } from '../../contexts/AuthContext';
import { useAppSettings, reloadAppSettingsCache } from '../../hooks/useAppSettings';
import { useVirtualTradingCore } from '@frontend/hooks/useVirtualTradingCore';
import NotificationListRow from './NotificationListRow';
import { VirtualScroll } from '@frontend/components/common/VirtualScroll';
import NotificationDetailScreen from './NotificationDetailScreen';
import VirtualTradeScreen from '../virtual/VirtualTradeScreen';
@@ -133,12 +134,11 @@ export default function NotificationsScreen() {
)}
<div
ref={listRef}
className="notify-list-wrap"
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
style={{ flex: 1, overflow: 'auto', WebkitOverflowScrolling: 'touch' }}
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}
>
{(pullY > 0 || refreshing) && (
<div className="notify-pull-hint" style={{ textAlign: 'center', padding: pullY > 0 ? `${pullY / 4}px` : 8, fontSize: 12, color: 'var(--gc-text-muted)' }}>
@@ -155,23 +155,34 @@ export default function NotificationsScreen() {
</button>
</div>
) : (
<div className="stack-list" style={{ padding: '0 16px' }}>
{allNotifications.map(item => (
<NotificationListRow
key={item.id}
item={item}
onDetail={() => {
markAsRead(item.id);
goNotifyDetail(item.id, item.market);
}}
onTrade={side => {
markAsRead(item.id);
goNotifyTrade(item.market, side);
}}
onDelete={() => void deleteNotification(item.id)}
/>
))}
</div>
<VirtualScroll
ref={listRef}
className="stack-list notify-virtual-list"
items={allNotifications}
estimateSize={148}
measureDynamic
overscan={5}
getItemKey={item => item.id}
innerClassName="vl-scroll-inner"
aria-label="알림 목록"
>
{item => (
<div style={{ padding: '0 16px 10px' }}>
<NotificationListRow
item={item}
onDetail={() => {
markAsRead(item.id);
goNotifyDetail(item.id, item.market);
}}
onTrade={side => {
markAsRead(item.id);
goNotifyTrade(item.market, side);
}}
onDelete={() => void deleteNotification(item.id)}
/>
</div>
)}
</VirtualScroll>
)}
</div>
@@ -11,6 +11,7 @@ import VirtualFocusScreen from './VirtualFocusScreen';
import VirtualTradeScreen from './VirtualTradeScreen';
import VirtualHistoryScreen from './VirtualHistoryScreen';
import { MarketSearchPanel } from '@frontend/components/MarketSearchPanel';
import { VirtualScroll } from '@frontend/components/common/VirtualScroll';
export default function VirtualTradingScreen() {
const {
@@ -172,15 +173,23 @@ export default function VirtualTradingScreen() {
)}
</div>
) : (
<div className="stack-list" style={{ padding: '0 16px' }}>
{targets.map(t => {
<VirtualScroll
className="stack-list virtual-target-virtual-list"
items={targets}
estimateSize={120}
measureDynamic
getItemKey={t => t.market}
innerClassName="vl-scroll-inner"
aria-label="투자 대상 목록"
>
{t => {
const strategyId = resolveVirtualTargetStrategyId(t, session.globalStrategyId);
const snapKey = `${t.market}:${strategyId ?? ''}`;
const snap = snapshots[snapKey] ?? snapshots[t.market];
const signalLoading = snapshotLoadingByMarket[t.market];
return (
<div style={{ padding: '0 16px 10px' }}>
<VirtualTargetListRow
key={t.market}
target={t}
globalStrategyId={session.globalStrategyId}
strategies={strategies}
@@ -192,9 +201,10 @@ export default function VirtualTradingScreen() {
onTogglePin={() => void handleTogglePin(t.market)}
onRemove={() => handleRemoveTarget(t.market)}
/>
</div>
);
})}
</div>
}}
</VirtualScroll>
)}
<button type="button" className="fab" aria-label="종목 추가" onClick={() => setAddSheetOpen(true)}>+</button>