From 5bff312b3b670964c0b906921b2e3d5ec0bb7c5f Mon Sep 17 00:00:00 2001 From: duckietm Date: Wed, 8 Apr 2026 14:06:25 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20Fixed=20some=20minor=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- postcss.config.js | 3 +- .../notification/NotificationBubbleItem.ts | 9 ++- src/common/layout/LayoutAvatarImageView.tsx | 65 +++++++------------ src/common/layout/LayoutFurniImageView.tsx | 31 ++++++--- src/common/layout/LayoutRoomPreviewerView.tsx | 22 +++++-- .../NotificationBadgeReceivedBubbleView.tsx | 6 +- src/css/index.css | 27 ++++++++ src/hooks/notification/useNotification.ts | 8 +-- tailwind.config.js | 24 ------- 9 files changed, 109 insertions(+), 86 deletions(-) diff --git a/postcss.config.js b/postcss.config.js index f16a034..f5c8618 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -2,7 +2,6 @@ module.exports = { plugins: { - '@tailwindcss/postcss': {}, - autoprefixer: {} + '@tailwindcss/postcss': {} } } diff --git a/src/api/notification/NotificationBubbleItem.ts b/src/api/notification/NotificationBubbleItem.ts index fe90dab..d04d445 100644 --- a/src/api/notification/NotificationBubbleItem.ts +++ b/src/api/notification/NotificationBubbleItem.ts @@ -9,8 +9,9 @@ export class NotificationBubbleItem private _notificationType: string; private _iconUrl: string; private _linkUrl: string; + private _senderName: string; - constructor(message: string, notificationType: string = NotificationBubbleType.INFO, iconUrl: string = null, linkUrl: string = null) + constructor(message: string, notificationType: string = NotificationBubbleType.INFO, iconUrl: string = null, linkUrl: string = null, senderName: string = '') { NotificationBubbleItem.ITEM_ID += 1; @@ -19,6 +20,7 @@ export class NotificationBubbleItem this._notificationType = notificationType; this._iconUrl = iconUrl; this._linkUrl = linkUrl; + this._senderName = senderName; } public get id(): number @@ -45,4 +47,9 @@ export class NotificationBubbleItem { return this._linkUrl; } + + public get senderName(): string + { + return this._senderName; + } } diff --git a/src/common/layout/LayoutAvatarImageView.tsx b/src/common/layout/LayoutAvatarImageView.tsx index fdde1ac..2b3b156 100644 --- a/src/common/layout/LayoutAvatarImageView.tsx +++ b/src/common/layout/LayoutAvatarImageView.tsx @@ -1,7 +1,6 @@ -import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager, NitroEventType } from '@nitrots/nitro-renderer'; +import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager } from '@nitrots/nitro-renderer'; import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react'; import { Base, BaseProps } from '../Base'; -import { useNitroEvent } from '../../hooks/events'; const AVATAR_IMAGE_CACHE: Map = new Map(); @@ -19,18 +18,7 @@ export const LayoutAvatarImageView: FC = props => const { figure = '', gender = '', headOnly = false, direction = 0, scale = 1, classNames = [], style = {}, ...rest } = props; const [ avatarUrl, setAvatarUrl ] = useState(null); const [ isReady, setIsReady ] = useState(false); - const [ updateId, setUpdateId ] = useState(0); const isDisposed = useRef(false); - const figureKeyRef = useRef(null); - - useNitroEvent(NitroEventType.AVATAR_ASSET_LOADED, () => - { - if(figureKeyRef.current) - { - AVATAR_IMAGE_CACHE.delete(figureKeyRef.current); - setUpdateId(prev => prev + 1); - } - }); const getClassNames = useMemo(() => { @@ -65,44 +53,39 @@ export const LayoutAvatarImageView: FC = props => const figureKey = [ figure, gender, direction, headOnly ].join('-'); - figureKeyRef.current = figureKey; - if(AVATAR_IMAGE_CACHE.has(figureKey)) { setAvatarUrl(AVATAR_IMAGE_CACHE.get(figureKey)); } else { - const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, { - resetFigure: (figure: string) => - { - if(isDisposed.current) return; - - AVATAR_IMAGE_CACHE.delete(figureKey); - setUpdateId(prev => prev + 1); - }, - dispose: null, - disposed: false - }); - - let setType = AvatarSetType.FULL; - - if(headOnly) setType = AvatarSetType.HEAD; - - avatarImage.setDirection(setType, direction); - - const imageUrl = avatarImage.processAsImageUrl(setType); - - if(imageUrl && !isDisposed.current) + const resetFigure = (_figure: string) => { - if(!avatarImage.isPlaceholder()) AVATAR_IMAGE_CACHE.set(figureKey, imageUrl); + if(isDisposed.current) return; - setAvatarUrl(imageUrl); - } + const avatarImage = GetAvatarRenderManager().createAvatarImage(_figure, AvatarScaleType.LARGE, gender, { resetFigure: (figure: string) => resetFigure(figure), dispose: null, disposed: false }); - avatarImage.dispose(); + let setType = AvatarSetType.FULL; + + if(headOnly) setType = AvatarSetType.HEAD; + + avatarImage.setDirection(setType, direction); + + const imageUrl = avatarImage.processAsImageUrl(setType); + + if(imageUrl && !isDisposed.current) + { + if(!avatarImage.isPlaceholder()) AVATAR_IMAGE_CACHE.set(figureKey, imageUrl); + + setAvatarUrl(imageUrl); + } + + avatarImage.dispose(); + }; + + resetFigure(figure); } - }, [ figure, gender, direction, headOnly, isReady, updateId ]); + }, [ figure, gender, direction, headOnly, isReady ]); useEffect(() => { diff --git a/src/common/layout/LayoutFurniImageView.tsx b/src/common/layout/LayoutFurniImageView.tsx index b83d811..f7fe9dc 100644 --- a/src/common/layout/LayoutFurniImageView.tsx +++ b/src/common/layout/LayoutFurniImageView.tsx @@ -1,5 +1,5 @@ import { GetRoomEngine, IGetImageListener, ImageResult, TextureUtils, Vector3d } from '@nitrots/nitro-renderer'; -import { CSSProperties, FC, useEffect, useMemo, useState } from 'react'; +import { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ProductTypeEnum } from '../../api'; import { Base, BaseProps } from '../Base'; @@ -16,6 +16,23 @@ export const LayoutFurniImageView: FC = props => { const { productType = 's', productClassId = -1, direction = 2, extraData = '', scale = 1, style = {}, ...rest } = props; const [ imageElement, setImageElement ] = useState(null); + const isMounted = useRef(true); + + useEffect(() => + { + isMounted.current = true; + + return () => { isMounted.current = false; }; + }, []); + + const updateImage = useCallback(async (texture: any) => + { + if(!texture) return; + + const image = await TextureUtils.generateImage(texture); + + if(image && isMounted.current) setImageElement(image); + }, []); const getStyle = useMemo(() => { @@ -42,10 +59,12 @@ export const LayoutFurniImageView: FC = props => useEffect(() => { + setImageElement(null); + let imageResult: ImageResult = null; const listener: IGetImageListener = { - imageReady: async (id, texture, image) => setImageElement(await TextureUtils.generateImage(texture)), + imageReady: (result) => updateImage(result?.data), imageFailed: null }; @@ -59,12 +78,8 @@ export const LayoutFurniImageView: FC = props => break; } - if(!imageResult) return; - - (async () => setImageElement(await TextureUtils.generateImage(imageResult.data)))(); - }, [ productType, productClassId, direction, extraData ]); - - if(!imageElement) return null; + if(imageResult?.data) updateImage(imageResult.data); + }, [ productType, productClassId, direction, extraData, updateImage ]); return ; }; diff --git a/src/common/layout/LayoutRoomPreviewerView.tsx b/src/common/layout/LayoutRoomPreviewerView.tsx index daceeef..ca1ef26 100644 --- a/src/common/layout/LayoutRoomPreviewerView.tsx +++ b/src/common/layout/LayoutRoomPreviewerView.tsx @@ -24,15 +24,13 @@ export const LayoutRoomPreviewerView: FC<{ const width = elementRef.current.parentElement.clientWidth; const texture = TextureUtils.createRenderTexture(width, height); - const update = async (ticker: NitroTicker) => + const paintToDOM = () => { if(!roomPreviewer || !elementRef.current) return; - roomPreviewer.updatePreviewRoomView(); - const renderingCanvas = roomPreviewer.getRenderingCanvas(); - if(!renderingCanvas.canvasUpdated) return; + if(!renderingCanvas) return; GetRenderer().render({ target: texture, @@ -48,6 +46,20 @@ export const LayoutRoomPreviewerView: FC<{ elementRef.current.style.backgroundImage = `url(${ base64 })`; }; + const update = (ticker: NitroTicker) => + { + if(!roomPreviewer || !elementRef.current) return; + + roomPreviewer.updatePreviewRoomView(); + + const renderingCanvas = roomPreviewer.getRenderingCanvas(); + + if(renderingCanvas && renderingCanvas.canvasUpdated) + { + paintToDOM(); + } + }; + GetTicker().add(update); const resizeObserver = new ResizeObserver(() => @@ -58,7 +70,7 @@ export const LayoutRoomPreviewerView: FC<{ roomPreviewer.modifyRoomCanvas(width, height); - update(GetTicker()); + paintToDOM(); }); roomPreviewer.getRoomCanvas(width, height); diff --git a/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx b/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx index 7b57db3..8c1154e 100644 --- a/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx +++ b/src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx @@ -45,7 +45,11 @@ export const NotificationBadgeReceivedBubbleView: FC } - { LocalizeText('notification.badge.received') } + + { item.senderName + ? LocalizeText('notifications.text.received.badge', [ 'user_name' ], [ item.senderName ]) + : LocalizeText('prereg.reward.you.received') } + { item.message } diff --git a/src/css/index.css b/src/css/index.css index 760427e..a857640 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -2,6 +2,33 @@ @config "../../tailwind.config.js"; +@theme { + --animate-pulse-glow: pulseGlow 1.2s ease-in-out infinite; + --animate-pulse-glow-red: pulseGlowRed 1.2s ease-in-out infinite; + --animate-drop-settle: dropSettle 0.3s ease-out; + --animate-pulse-glow-gold: pulseGlowGold 1.5s ease-in-out infinite; +} + +@keyframes pulseGlow { + 0%, 100% { box-shadow: 0 0 6px rgba(59, 130, 246, 0.3); } + 50% { box-shadow: 0 0 14px rgba(59, 130, 246, 0.6); } +} + +@keyframes pulseGlowRed { + 0%, 100% { box-shadow: 0 0 6px rgba(239, 68, 68, 0.3); } + 50% { box-shadow: 0 0 14px rgba(239, 68, 68, 0.6); } +} + +@keyframes dropSettle { + 0% { transform: scale(1.15); } + 100% { transform: scale(1); } +} + +@keyframes pulseGlowGold { + 0%, 100% { box-shadow: 0 0 6px rgba(255, 193, 7, 0.4); } + 50% { box-shadow: 0 0 14px rgba(255, 193, 7, 0.7); } +} + @font-face { font-family: Ubuntu; src: url("@/assets/webfonts/Ubuntu-C.ttf"); diff --git a/src/hooks/notification/useNotification.ts b/src/hooks/notification/useNotification.ts index 007a61c..911fd63 100644 --- a/src/hooks/notification/useNotification.ts +++ b/src/hooks/notification/useNotification.ts @@ -68,11 +68,11 @@ const useNotificationState = () => const showNitroAlert = useCallback(() => simpleAlert(null, NotificationAlertType.NITRO), [ simpleAlert ]); - const showSingleBubble = useCallback((message: string, type: string, imageUrl: string = null, internalLink: string = null) => + const showSingleBubble = useCallback((message: string, type: string, imageUrl: string = null, internalLink: string = null, senderName: string = '') => { if(bubblesDisabled) return; - const notificationItem = new NotificationBubbleItem(message, type, imageUrl, internalLink); + const notificationItem = new NotificationBubbleItem(message, type, imageUrl, internalLink, senderName); setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]); }, [ bubblesDisabled ]); @@ -226,7 +226,6 @@ const useNotificationState = () => { const parser = event.getParser(); - // Skip if AchievementNotificationMessageEvent already showed a notification for this badge if(recentBadgeNotifications.has(parser.badgeCode)) return; recentBadgeNotifications.add(parser.badgeCode); @@ -234,8 +233,9 @@ const useNotificationState = () => const badgeName = LocalizeBadgeName(parser.badgeCode); const badgeImage = GetSessionDataManager().getBadgeUrl(parser.badgeCode); + const senderName = parser.senderName || ''; - showSingleBubble(badgeName, NotificationBubbleType.BADGE_RECEIVED, badgeImage, parser.badgeCode); + showSingleBubble(badgeName, NotificationBubbleType.BADGE_RECEIVED, badgeImage, parser.badgeCode, senderName); }); useMessageEvent(ClubGiftNotificationEvent, event => diff --git a/tailwind.config.js b/tailwind.config.js index 6177ad9..d1bdbd6 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -102,30 +102,6 @@ module.exports = { dropShadow: { 'hover': '2px 2px 0 rgba(0,0,0,0.8)' }, - keyframes: { - pulseGlow: { - '0%, 100%': { boxShadow: '0 0 6px rgba(59,130,246,0.3)' }, - '50%': { boxShadow: '0 0 14px rgba(59,130,246,0.6)' }, - }, - pulseGlowRed: { - '0%, 100%': { boxShadow: '0 0 6px rgba(239,68,68,0.3)' }, - '50%': { boxShadow: '0 0 14px rgba(239,68,68,0.6)' }, - }, - dropSettle: { - '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', - }, }, }, safelist: [