알림 목록 정렬 기능 추가

This commit is contained in:
Macbook
2026-05-29 20:55:21 +09:00
parent 775f83e1b3
commit 6556741f83
3 changed files with 74 additions and 2 deletions
@@ -23,9 +23,12 @@ import TradeNotificationGridLayoutPicker from './tradeNotification/TradeNotifica
import { import {
loadTradeNotificationGridPreset, loadTradeNotificationGridPreset,
loadTradeNotificationListLayout, loadTradeNotificationListLayout,
loadTradeNotificationListSort,
saveTradeNotificationGridPreset, saveTradeNotificationGridPreset,
saveTradeNotificationListLayout, saveTradeNotificationListLayout,
saveTradeNotificationListSort,
type TradeNotificationListLayout, type TradeNotificationListLayout,
type TradeNotificationListSort,
} from '../utils/tradeNotificationListLayout'; } from '../utils/tradeNotificationListLayout';
import { import {
getTradeNotificationGridPreset, getTradeNotificationGridPreset,
@@ -74,6 +77,24 @@ function fillBothSides(
setFillSell({ market, price, side: 'sell', seq: seq + 1 }); setFillSell({ market, price, side: 'sell', seq: seq + 1 });
} }
function sortNotifications(
items: TradeNotificationItem[],
sort: TradeNotificationListSort,
): TradeNotificationItem[] {
const arr = [...items];
if (sort === 'market') {
return arr.sort((a, b) => {
const byMarket = a.market.localeCompare(b.market, 'ko');
if (byMarket !== 0) return byMarket;
return b.receivedAt - a.receivedAt;
});
}
if (sort === 'oldest') {
return arr.sort((a, b) => a.receivedAt - b.receivedAt);
}
return arr.sort((a, b) => b.receivedAt - a.receivedAt);
}
export const TradeNotificationListPage: React.FC<Props> = ({ export const TradeNotificationListPage: React.FC<Props> = ({
theme, theme,
onGoToChart, onGoToChart,
@@ -96,6 +117,9 @@ export const TradeNotificationListPage: React.FC<Props> = ({
} = useTradeNotification(); } = useTradeNotification();
const [filter, setFilter] = useState<'all' | 'unread'>('all'); const [filter, setFilter] = useState<'all' | 'unread'>('all');
const [listSort, setListSort] = useState<TradeNotificationListSort>(
() => loadTradeNotificationListSort(),
);
const [listLayout, setListLayout] = useState<TradeNotificationListLayout>( const [listLayout, setListLayout] = useState<TradeNotificationListLayout>(
() => loadTradeNotificationListLayout(), () => loadTradeNotificationListLayout(),
); );
@@ -226,6 +250,16 @@ export const TradeNotificationListPage: React.FC<Props> = ({
? allNotifications.filter(n => !n.isRead) ? allNotifications.filter(n => !n.isRead)
: allNotifications; : allNotifications;
const sorted = useMemo(
() => sortNotifications(filtered, listSort),
[filtered, listSort],
);
const handleListSortChange = useCallback((sort: TradeNotificationListSort) => {
setListSort(sort);
saveTradeNotificationListSort(sort);
}, []);
const handleListLayoutChange = useCallback((layout: TradeNotificationListLayout) => { const handleListLayoutChange = useCallback((layout: TradeNotificationListLayout) => {
setListLayout(layout); setListLayout(layout);
saveTradeNotificationListLayout(layout); saveTradeNotificationListLayout(layout);
@@ -303,6 +337,29 @@ export const TradeNotificationListPage: React.FC<Props> = ({
</button> </button>
)} )}
<div className="tnl-filter tnl-sort-toggle" role="group" aria-label="목록 정렬">
<button
type="button"
className={listSort === 'market' ? 'tnl-filter-btn--active' : ''}
onClick={() => handleListSortChange('market')}
>
</button>
<button
type="button"
className={listSort === 'newest' ? 'tnl-filter-btn--active' : ''}
onClick={() => handleListSortChange('newest')}
>
</button>
<button
type="button"
className={listSort === 'oldest' ? 'tnl-filter-btn--active' : ''}
onClick={() => handleListSortChange('oldest')}
>
</button>
</div>
<div className="tnl-toolbar-end"> <div className="tnl-toolbar-end">
<div className="tnl-layout-toggle vtd-view-toggle" role="group" aria-label="목록 표시 방식"> <div className="tnl-layout-toggle vtd-view-toggle" role="group" aria-label="목록 표시 방식">
<button <button
@@ -361,7 +418,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
</div> </div>
<div className={['tnl-list-wrap', !isListView ? 'tnl-list-wrap--grid' : ''].filter(Boolean).join(' ')}> <div className={['tnl-list-wrap', !isListView ? 'tnl-list-wrap--grid' : ''].filter(Boolean).join(' ')}>
{filtered.length === 0 ? ( {sorted.length === 0 ? (
<div className="tnl-empty"> .</div> <div className="tnl-empty"> .</div>
) : ( ) : (
<ul <ul
@@ -372,7 +429,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
].filter(Boolean).join(' ')} ].filter(Boolean).join(' ')}
style={!isListView ? { gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))` } : undefined} style={!isListView ? { gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))` } : undefined}
> >
{filtered.map(item => ( {sorted.map(item => (
<TradeNotificationListRow <TradeNotificationListRow
key={item.id} key={item.id}
item={item} item={item}
+2
View File
@@ -35,6 +35,8 @@ export interface UiPreferences {
listLayout?: 'list' | 'grid'; listLayout?: 'list' | 'grid';
/** 그리드형 열 배치 프리셋 id (tradeNotificationGridPresets) */ /** 그리드형 열 배치 프리셋 id (tradeNotificationGridPresets) */
gridPreset?: string; gridPreset?: string;
/** 목록 정렬 — market | newest | oldest */
listSort?: 'market' | 'newest' | 'oldest';
}; };
} }
@@ -7,6 +7,9 @@ import {
export type TradeNotificationListLayout = 'list' | 'grid'; export type TradeNotificationListLayout = 'list' | 'grid';
/** 알림 목록 정렬 */
export type TradeNotificationListSort = 'market' | 'newest' | 'oldest';
export function loadTradeNotificationListLayout(): TradeNotificationListLayout { export function loadTradeNotificationListLayout(): TradeNotificationListLayout {
const v = getUiPreferences().tradeNotifications?.listLayout; const v = getUiPreferences().tradeNotifications?.listLayout;
return v === 'grid' ? 'grid' : 'list'; return v === 'grid' ? 'grid' : 'list';
@@ -25,3 +28,13 @@ export function loadTradeNotificationGridPreset(): TradeNotificationGridPresetId
export function saveTradeNotificationGridPreset(preset: TradeNotificationGridPresetId): void { export function saveTradeNotificationGridPreset(preset: TradeNotificationGridPresetId): void {
patchUiPreferences({ tradeNotifications: { gridPreset: preset } }); patchUiPreferences({ tradeNotifications: { gridPreset: preset } });
} }
export function loadTradeNotificationListSort(): TradeNotificationListSort {
const v = getUiPreferences().tradeNotifications?.listSort;
if (v === 'market' || v === 'oldest') return v;
return 'newest';
}
export function saveTradeNotificationListSort(sort: TradeNotificationListSort): void {
patchUiPreferences({ tradeNotifications: { listSort: sort } });
}