알림 전체닫기 오동작 수정
This commit is contained in:
@@ -160,8 +160,12 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
||||
const refreshHistory = useCallback(async (opts?: { serverOnly?: boolean; rawServerList?: boolean }) => {
|
||||
try {
|
||||
const dtos = await loadTradeSignals();
|
||||
/** dismiss 직후 비동기 완료 시 state readIds가 늦게 반영되는 것 방지 */
|
||||
const read = opts?.rawServerList ? new Set<string>() : loadReadIds();
|
||||
/**
|
||||
* 항상 DB 캐시의 최신 readIds 사용.
|
||||
* rawServerList=true 시 빈 Set을 쓰던 기존 코드는 새로고침 후 모든 알림이
|
||||
* 미읽음으로 복원되는 버그를 유발했으므로 제거.
|
||||
*/
|
||||
const read = loadReadIds();
|
||||
const hidden = opts?.rawServerList ? new Set<string>() : loadHiddenIds();
|
||||
const items = dtos
|
||||
.map(d => dtoToItem(d, read))
|
||||
@@ -170,9 +174,16 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
||||
let newlyArrived: TradeNotificationItem[] = [];
|
||||
|
||||
setAllNotifications(prev => {
|
||||
newlyArrived = items.filter(
|
||||
item => !item.isRead && !prev.some(p => p.id === item.id),
|
||||
);
|
||||
/**
|
||||
* rawServerList=true 는 앱 초기 로드(새로고침·로그인 직후)이므로
|
||||
* 이미 존재하는 서버 이력을 실시간 신규 알림으로 취급하지 않는다.
|
||||
* 실제 새 알림 판단은 STOMP addNotification 과 일반 refreshHistory 에서만 한다.
|
||||
*/
|
||||
if (!opts?.rawServerList) {
|
||||
newlyArrived = items.filter(
|
||||
item => !item.isRead && !prev.some(p => p.id === item.id),
|
||||
);
|
||||
}
|
||||
|
||||
if (opts?.serverOnly || opts?.rawServerList) {
|
||||
return items
|
||||
@@ -234,15 +245,12 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
||||
|
||||
const addNotification = useCallback((signal: TradeSignalInfo & { dbId?: number }) => {
|
||||
const id = makeId(signal);
|
||||
// 실시간 재수신 시 이전에 읽음 처리된 동일 키도 다시 미확인으로
|
||||
setReadIds(prev => {
|
||||
if (!prev.has(id)) return prev;
|
||||
const next = new Set(prev);
|
||||
next.delete(id);
|
||||
saveReadIds(next);
|
||||
readIdsRef.current = next;
|
||||
return next;
|
||||
});
|
||||
/**
|
||||
* 이전에 읽음 처리(닫기)한 동일 키를 readIds 에서 제거하던 로직을 삭제.
|
||||
* STOMP 재연결 시 동일 신호가 재수신되어도 사용자가 명시적으로 닫은 알림은
|
||||
* 다시 열리지 않아야 한다. 새로운 candleTime 을 가진 신호는 키가 달라
|
||||
* readIds 에 없으므로 정상적으로 미읽음 처리된다.
|
||||
*/
|
||||
const item: TradeNotificationItem = {
|
||||
...signal,
|
||||
id,
|
||||
@@ -311,28 +319,23 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
||||
}, []);
|
||||
|
||||
const dismissAllToasts = useCallback(() => {
|
||||
const snapshot = toastNotificationsRef.current;
|
||||
if (snapshot.length === 0) return;
|
||||
const idSet = new Set(snapshot.map(n => n.id));
|
||||
|
||||
setReadIds(r => {
|
||||
const next = new Set(r);
|
||||
idSet.forEach(i => next.add(i));
|
||||
saveReadIds(next);
|
||||
readIdsRef.current = next;
|
||||
return next;
|
||||
/**
|
||||
* 전체 닫기 = 현재 토스트 + allNotifications 의 모든 미읽음 항목을 읽음 처리.
|
||||
* 기존에는 toastNotifications 에 있는 항목만 처리해서, 토스트에 없는 미읽음 항목은
|
||||
* 다음 refreshHistory 폴링 시 다시 팝업으로 나타나는 문제가 있었다.
|
||||
*/
|
||||
setAllNotifications(prev => {
|
||||
const nextRead = new Set(readIdsRef.current);
|
||||
// 토스트 항목
|
||||
toastNotificationsRef.current.forEach(n => nextRead.add(n.id));
|
||||
// allNotifications 의 미읽음 항목 전체
|
||||
prev.forEach(n => nextRead.add(n.id));
|
||||
saveReadIds(nextRead);
|
||||
readIdsRef.current = nextRead;
|
||||
setReadIds(nextRead);
|
||||
return prev.map(n => ({ ...n, isRead: true }));
|
||||
});
|
||||
setToastNotifications([]);
|
||||
setAllNotifications(all => {
|
||||
const byId = new Map(all.map(n => [n.id, n]));
|
||||
for (const n of snapshot) {
|
||||
const existing = byId.get(n.id);
|
||||
byId.set(n.id, existing ? { ...existing, isRead: true } : { ...n, isRead: true });
|
||||
}
|
||||
return [...byId.values()]
|
||||
.sort((a, b) => b.receivedAt - a.receivedAt)
|
||||
.slice(0, MAX_HISTORY);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const markAllAsRead = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user