알림 목록 정렬 기능 추가
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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 } });
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user