팝업알림 오류
This commit is contained in:
@@ -223,18 +223,41 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
let newlyArrived: TradeNotificationItem[] = [];
|
let newlyArrived: TradeNotificationItem[] = [];
|
||||||
|
|
||||||
setAllNotifications(prev => {
|
setAllNotifications(prev => {
|
||||||
/**
|
|
||||||
* rawServerList=true 는 앱 초기 로드(새로고침·로그인 직후)이므로
|
|
||||||
* 이미 존재하는 서버 이력을 실시간 신규 알림으로 취급하지 않는다.
|
|
||||||
* 실제 새 알림 판단은 STOMP addNotification 과 일반 refreshHistory 에서만 한다.
|
|
||||||
*/
|
|
||||||
if (!opts?.rawServerList) {
|
if (!opts?.rawServerList) {
|
||||||
|
/**
|
||||||
|
* 일반 폴링·serverOnly 경로: 이미 목록에 없는 미읽음 신호만 신규 도착으로 판정.
|
||||||
|
*/
|
||||||
newlyArrived = items.filter(
|
newlyArrived = items.filter(
|
||||||
item =>
|
item =>
|
||||||
!item.isRead &&
|
!item.isRead &&
|
||||||
!readIdsRef.current.has(item.id) && // readIds 에 없는(진짜 새) 알림만
|
!readIdsRef.current.has(item.id) && // readIds 에 없는(진짜 새) 알림만
|
||||||
!prev.some(p => p.id === item.id), // 이미 목록에 없는 알림만
|
!prev.some(p => p.id === item.id), // 이미 목록에 없는 알림만
|
||||||
);
|
);
|
||||||
|
} else if (popupEnabled) {
|
||||||
|
/**
|
||||||
|
* rawServerList=true (앱 시작·로그인 직후) + 팝업 ON:
|
||||||
|
*
|
||||||
|
* 기존 로직은 서버 이력을 "실시간 신규 알림"으로 취급하지 않아
|
||||||
|
* suppressBefore 이후 생성된 미읽음 신호가 배지에는 표시되지만
|
||||||
|
* 팝업은 전혀 뜨지 않는 문제가 있었다.
|
||||||
|
*
|
||||||
|
* 서버 재시작·짧은 연결 끊김 등으로 STOMP 를 놓친 신호도 팝업으로
|
||||||
|
* 보여줘야 하므로, suppressBefore 이후에 생성된 미읽음 신호 전체를
|
||||||
|
* newlyArrived 에 포함시킨다.
|
||||||
|
*
|
||||||
|
* suppressBefore == 0 (전체닫기 이력 없음) 인 경우 최근 2시간 이내
|
||||||
|
* 신호로 범위를 제한해 과도한 팝업을 방지한다.
|
||||||
|
*/
|
||||||
|
const cutoff = suppressBeforeRef.current > 0
|
||||||
|
? suppressBeforeRef.current * 1000 // 마지막 전체닫기 이후 신호
|
||||||
|
: Date.now() - 2 * 60 * 60 * 1000; // 또는 최근 2시간
|
||||||
|
|
||||||
|
newlyArrived = items.filter(
|
||||||
|
item =>
|
||||||
|
!item.isRead &&
|
||||||
|
!readIdsRef.current.has(item.id) &&
|
||||||
|
item.receivedAt > cutoff,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts?.serverOnly || opts?.rawServerList) {
|
if (opts?.serverOnly || opts?.rawServerList) {
|
||||||
@@ -266,7 +289,8 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
for (const n of newlyArrived) map.set(n.id, n);
|
for (const n of newlyArrived) map.set(n.id, n);
|
||||||
return [...map.values()].slice(0, MAX_TOAST_QUEUE);
|
return [...map.values()].slice(0, MAX_TOAST_QUEUE);
|
||||||
});
|
});
|
||||||
if (soundEnabled) {
|
// rawServerList (앱 시작) 시 기존 미읽음 신호 팝업은 사운드 없이 조용히 표시
|
||||||
|
if (soundEnabled && !opts?.rawServerList) {
|
||||||
playTradeAlertSound(alertSoundId);
|
playTradeAlertSound(alertSoundId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,13 +299,19 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
}
|
}
|
||||||
}, [popupEnabled, soundEnabled, alertSoundId]);
|
}, [popupEnabled, soundEnabled, alertSoundId]);
|
||||||
|
|
||||||
|
const prevSessionKeyRef = useRef(settingsSessionKey);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!settingsLoaded) return;
|
if (!settingsLoaded) return;
|
||||||
|
|
||||||
|
const isSessionSwitch = prevSessionKeyRef.current !== settingsSessionKey;
|
||||||
|
prevSessionKeyRef.current = settingsSessionKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 앱 설정이 DB 에서 로드 완료된 시점에 readIds 를 즉시 동기화한다.
|
* 앱 설정이 DB 에서 로드 완료된 시점에 readIds 를 즉시 동기화한다.
|
||||||
* STOMP 가 앱 시작 직후 구독 시 readIds 가 아직 빈 상태여서 이미 닫은
|
* STOMP 가 앱 시작 직후 구독 시 readIds 가 아직 빈 상태여서 이미 닫은
|
||||||
* 알림이 다시 팝업으로 뜨는 타이밍 버그를 방지한다.
|
* 알림이 다시 팝업으로 뜨는 타이밍 버그를 방지한다.
|
||||||
* 세션 전환 시 allSeenIdsRef 도 초기화해 이전 세션 알림 ID 가 잔류하지 않게 한다.
|
* 세션 전환(로그인·로그아웃) 시 allSeenIdsRef 를 초기화해 이전 세션 ID 잔류 방지.
|
||||||
*/
|
*/
|
||||||
allSeenIdsRef.current = new Set();
|
allSeenIdsRef.current = new Set();
|
||||||
const dbRead = loadReadIds();
|
const dbRead = loadReadIds();
|
||||||
@@ -292,7 +322,17 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
}
|
}
|
||||||
// 전체닫기·삭제 시각 복원 (재접속 후에도 오래된 신호 팝업 방지)
|
// 전체닫기·삭제 시각 복원 (재접속 후에도 오래된 신호 팝업 방지)
|
||||||
suppressBeforeRef.current = loadSuppressBefore();
|
suppressBeforeRef.current = loadSuppressBefore();
|
||||||
setToastNotifications([]);
|
|
||||||
|
/**
|
||||||
|
* [버그 수정] 기존 코드는 항상 setToastNotifications([]) 로 초기화했는데,
|
||||||
|
* 이 경우 STOMP 가 설정 로드보다 먼저 도착한 토스트까지 소거되는 race condition 이 있었다.
|
||||||
|
* 세션 전환(로그인·로그아웃) 시에만 초기화하고, 단순 설정 로드 완료 시에는
|
||||||
|
* 초기화하지 않는다 — rawServerList 로드가 최신 토스트를 올바르게 추가한다.
|
||||||
|
*/
|
||||||
|
if (isSessionSwitch) {
|
||||||
|
setToastNotifications([]);
|
||||||
|
}
|
||||||
|
|
||||||
void refreshHistory({ serverOnly: true, rawServerList: true });
|
void refreshHistory({ serverOnly: true, rawServerList: true });
|
||||||
}, [refreshHistory, settingsLoaded, settingsSessionKey]);
|
}, [refreshHistory, settingsLoaded, settingsSessionKey]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user