Fix badge notification: single bubble, working Wear button

Block NotificationDialogMessageEvent badge types to prevent duplicate
bubbles. Wrap bubble content in stopPropagation div so button clicks
don't close the notification before toggleBadge runs. Request badge
data on mount so Wear works even if inventory was never opened.
This commit is contained in:
Life
2026-04-04 18:45:19 +02:00
parent bc6a33a3ba
commit 0b033742d0
2 changed files with 47 additions and 24 deletions
@@ -1,5 +1,6 @@
import { FC } from 'react'; import { RequestBadgesComposer } from '@nitrots/nitro-renderer';
import { LocalizeText, NotificationBubbleItem } from '../../../../api'; import { FC, useEffect } from 'react';
import { LocalizeText, NotificationBubbleItem, SendMessageComposer } from '../../../../api';
import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common'; import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common';
import { useInventoryBadges } from '../../../../hooks'; import { useInventoryBadges } from '../../../../hooks';
@@ -11,36 +12,55 @@ export interface NotificationBadgeReceivedBubbleViewProps extends LayoutNotifica
export const NotificationBadgeReceivedBubbleView: FC<NotificationBadgeReceivedBubbleViewProps> = props => export const NotificationBadgeReceivedBubbleView: FC<NotificationBadgeReceivedBubbleViewProps> = props =>
{ {
const { item = null, onClose = null, ...rest } = props; const { item = null, onClose = null, ...rest } = props;
const { toggleBadge = null } = useInventoryBadges(); const { badgeCodes = [], toggleBadge = null } = useInventoryBadges();
const handleWear = () => useEffect(() =>
{ {
if(item.linkUrl) toggleBadge(item.linkUrl); if(badgeCodes.length === 0) SendMessageComposer(new RequestBadgesComposer());
onClose(); }, []);
const handleWear = (event: React.MouseEvent) =>
{
event.stopPropagation();
if(item.linkUrl)
{
toggleBadge(item.linkUrl);
}
if(onClose) onClose();
};
const handleDismiss = (event: React.MouseEvent) =>
{
event.stopPropagation();
if(onClose) onClose();
}; };
return ( return (
<LayoutNotificationBubbleView className="flex-col" onClose={ onClose } { ...rest }> <LayoutNotificationBubbleView className="flex-col" onClose={ onClose } { ...rest }>
<Flex alignItems="center" gap={ 2 } className="mb-2"> <div onClick={ (e) => e.stopPropagation() }>
<Flex center className="w-[50px] h-[50px] shrink-0"> <Flex alignItems="center" gap={ 2 } className="mb-2">
{ item.iconUrl && <img alt="" className="no-select" src={ item.iconUrl } /> } <Flex center className="w-[50px] h-[50px] shrink-0">
{ item.iconUrl && <img alt="" className="no-select" src={ item.iconUrl } /> }
</Flex>
<Flex column gap={ 0 }>
<Text bold variant="white">{ LocalizeText('notification.badge.received') }</Text>
<Text variant="white" small>{ item.message }</Text>
</Flex>
</Flex> </Flex>
<Flex column gap={ 0 }> <Flex alignItems="center" justifyContent="end" gap={ 2 }>
<Text bold variant="white">{ LocalizeText('notification.badge.received') }</Text> <button
<Text variant="white" small>{ item.message }</Text> className="btn btn-success w-full btn-sm"
type="button"
onClick={ handleWear }>
{ LocalizeText('inventory.badges.wearbadge') }
</button>
<span className="underline cursor-pointer text-nowrap" onClick={ handleDismiss }>
{ LocalizeText('notifications.button.later') }
</span>
</Flex> </Flex>
</Flex> </div>
<Flex alignItems="center" justifyContent="end" gap={ 2 }>
<button
className="btn btn-success w-full btn-sm"
type="button"
onClick={ handleWear }>
{ LocalizeText('inventory.badges.wearbadge') }
</button>
<span className="underline cursor-pointer text-nowrap" onClick={ onClose }>
{ LocalizeText('notifications.button.later') }
</span>
</Flex>
</LayoutNotificationBubbleView> </LayoutNotificationBubbleView>
); );
}; };
@@ -356,6 +356,9 @@ const useNotificationState = () =>
{ {
const parser = event.getParser(); const parser = event.getParser();
// Skip badge notifications — handled by BadgeReceivedEvent with "Wear" button
if(parser.type === 'badge_received' || parser.type === 'badges' || parser.type.includes('badge')) return;
showNotification(parser.type, parser.parameters); showNotification(parser.type, parser.parameters);
}); });