From 020db838700f60a4cd749686e9eecf0c7eee087c Mon Sep 17 00:00:00 2001 From: Life Date: Sat, 4 Apr 2026 13:55:29 +0200 Subject: [PATCH] Add golden glow for new badges and badge received toast notification New unseen badges pulse with a gold glow instead of a flat green background. When a badge is received, a bubble notification appears with the badge image, name, and a "Wear" button that opens inventory. --- .../views/bubble-layouts/GetBubbleLayout.tsx | 3 ++ .../NotificationBadgeReceivedBubbleView.tsx | 38 +++++++++++++++++++ src/hooks/notification/useNotification.ts | 11 +++++- src/layout/InfiniteGrid.tsx | 2 +- tailwind.config.js | 8 +++- 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx diff --git a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx index f1298a4..f86f0f6 100644 --- a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx +++ b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx @@ -1,4 +1,5 @@ import { NotificationBubbleItem, NotificationBubbleType } from '../../../../api'; +import { NotificationBadgeReceivedBubbleView } from './NotificationBadgeReceivedBubbleView'; import { NotificationClubGiftBubbleView } from './NotificationClubGiftBubbleView'; import { NotificationDefaultBubbleView } from './NotificationDefaultBubbleView'; @@ -10,6 +11,8 @@ export const GetBubbleLayout = (item: NotificationBubbleItem, onClose: () => voi switch(item.notificationType) { + case NotificationBubbleType.BADGE_RECEIVED: + return ; case NotificationBubbleType.CLUBGIFT: return ; default: diff --git a/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx b/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx new file mode 100644 index 0000000..380c121 --- /dev/null +++ b/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx @@ -0,0 +1,38 @@ +import { FC } from 'react'; +import { LocalizeText, NotificationBubbleItem, OpenUrl } from '../../../../api'; +import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common'; + +export interface NotificationBadgeReceivedBubbleViewProps extends LayoutNotificationBubbleViewProps +{ + item: NotificationBubbleItem; +} + +export const NotificationBadgeReceivedBubbleView: FC = props => +{ + const { item = null, onClose = null, ...rest } = props; + + return ( + + + + { item.iconUrl && } + + + { LocalizeText('notification.badge.received') } + { item.message } + + + + + + { LocalizeText('notifications.button.later') } + + + + ); +}; diff --git a/src/hooks/notification/useNotification.ts b/src/hooks/notification/useNotification.ts index 41e85d1..8fea817 100644 --- a/src/hooks/notification/useNotification.ts +++ b/src/hooks/notification/useNotification.ts @@ -1,4 +1,4 @@ -import { AchievementNotificationMessageEvent, ActivityPointNotificationMessageEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, ConnectionErrorEvent, GetLocalizationManager, GetRoomEngine, GetSessionDataManager, HabboBroadcastMessageEvent, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelWillCloseInMinutesEvent, InfoFeedEnableMessageEvent, MaintenanceStatusMessageEvent, ModeratorCautionEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, PetLevelNotificationEvent, PetReceivedMessageEvent, RespectReceivedEvent, RoomEnterEffect, RoomEnterEvent, SimpleAlertMessageEvent, UserBannedMessageEvent, Vector3d, WiredRewardResultMessageEvent } from '@nitrots/nitro-renderer'; +import { AchievementNotificationMessageEvent, ActivityPointNotificationMessageEvent, BadgeReceivedEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, ConnectionErrorEvent, GetLocalizationManager, GetRoomEngine, GetSessionDataManager, HabboBroadcastMessageEvent, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelWillCloseInMinutesEvent, InfoFeedEnableMessageEvent, MaintenanceStatusMessageEvent, ModeratorCautionEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, PetLevelNotificationEvent, PetReceivedMessageEvent, RespectReceivedEvent, RoomEnterEffect, RoomEnterEvent, SimpleAlertMessageEvent, UserBannedMessageEvent, Vector3d, WiredRewardResultMessageEvent } from '@nitrots/nitro-renderer'; import { useCallback, useState } from 'react'; import { useBetween } from 'use-between'; import { GetConfigurationValue, LocalizeBadgeName, LocalizeText, NotificationAlertItem, NotificationAlertType, NotificationBubbleItem, NotificationBubbleType, NotificationConfirmItem, PlaySound, ProductImageUtility, TradingNotificationType } from '../../api'; @@ -217,6 +217,15 @@ const useNotificationState = () => showSingleBubble((text1 + ' ' + badgeName), NotificationBubbleType.ACHIEVEMENT, badgeImage, internalLink); }); + useMessageEvent(BadgeReceivedEvent, event => + { + const parser = event.getParser(); + const badgeName = LocalizeBadgeName(parser.badgeCode); + const badgeImage = GetSessionDataManager().getBadgeUrl(parser.badgeCode); + + showSingleBubble(badgeName, NotificationBubbleType.BADGE_RECEIVED, badgeImage, 'inventory/toggle'); + }); + useMessageEvent(ClubGiftNotificationEvent, event => { const parser = event.getParser(); diff --git a/src/layout/InfiniteGrid.tsx b/src/layout/InfiniteGrid.tsx index cb58e75..147ae01 100644 --- a/src/layout/InfiniteGrid.tsx +++ b/src/layout/InfiniteGrid.tsx @@ -172,7 +172,7 @@ const InfiniteGridItem = forwardRef 0)) && 'unique-item', itemUniqueSoldout && 'sold-out', - itemUnseen && ' bg-green-500 bg-opacity-40', + itemUnseen && ' animate-pulse-glow-gold border-yellow-400/60', className ) } style={ styleNames( diff --git a/tailwind.config.js b/tailwind.config.js index 26a5564..6177ad9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -115,11 +115,16 @@ module.exports = { '0%': { transform: 'scale(1.15)' }, '100%': { transform: 'scale(1)' }, }, + pulseGlowGold: { + '0%, 100%': { boxShadow: '0 0 6px rgba(255,193,7,0.4)' }, + '50%': { boxShadow: '0 0 14px rgba(255,193,7,0.7)' }, + }, }, animation: { 'pulse-glow': 'pulseGlow 1.2s ease-in-out infinite', 'pulse-glow-red': 'pulseGlowRed 1.2s ease-in-out infinite', 'drop-settle': 'dropSettle 0.3s ease-out', + 'pulse-glow-gold': 'pulseGlowGold 1.5s ease-in-out infinite', }, }, }, @@ -166,7 +171,8 @@ module.exports = { 'items-end', 'animate-pulse-glow', 'animate-pulse-glow-red', - 'animate-drop-settle' + 'animate-drop-settle', + 'animate-pulse-glow-gold' ], darkMode: 'class', variants: {