mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 06:56:20 +00:00
🆙 Fixed some minor bugs
This commit is contained in:
+1
-2
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
'@tailwindcss/postcss': {}
|
||||||
autoprefixer: {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ export class NotificationBubbleItem
|
|||||||
private _notificationType: string;
|
private _notificationType: string;
|
||||||
private _iconUrl: string;
|
private _iconUrl: string;
|
||||||
private _linkUrl: 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;
|
NotificationBubbleItem.ITEM_ID += 1;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ export class NotificationBubbleItem
|
|||||||
this._notificationType = notificationType;
|
this._notificationType = notificationType;
|
||||||
this._iconUrl = iconUrl;
|
this._iconUrl = iconUrl;
|
||||||
this._linkUrl = linkUrl;
|
this._linkUrl = linkUrl;
|
||||||
|
this._senderName = senderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): number
|
public get id(): number
|
||||||
@@ -45,4 +47,9 @@ export class NotificationBubbleItem
|
|||||||
{
|
{
|
||||||
return this._linkUrl;
|
return this._linkUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get senderName(): string
|
||||||
|
{
|
||||||
|
return this._senderName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Base, BaseProps } from '../Base';
|
import { Base, BaseProps } from '../Base';
|
||||||
import { useNitroEvent } from '../../hooks/events';
|
|
||||||
|
|
||||||
const AVATAR_IMAGE_CACHE: Map<string, string> = new Map();
|
const AVATAR_IMAGE_CACHE: Map<string, string> = new Map();
|
||||||
|
|
||||||
@@ -19,18 +18,7 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
|||||||
const { figure = '', gender = '', headOnly = false, direction = 0, scale = 1, classNames = [], style = {}, ...rest } = props;
|
const { figure = '', gender = '', headOnly = false, direction = 0, scale = 1, classNames = [], style = {}, ...rest } = props;
|
||||||
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
||||||
const [ isReady, setIsReady ] = useState<boolean>(false);
|
const [ isReady, setIsReady ] = useState<boolean>(false);
|
||||||
const [ updateId, setUpdateId ] = useState<number>(0);
|
|
||||||
const isDisposed = useRef(false);
|
const isDisposed = useRef(false);
|
||||||
const figureKeyRef = useRef<string>(null);
|
|
||||||
|
|
||||||
useNitroEvent(NitroEventType.AVATAR_ASSET_LOADED, () =>
|
|
||||||
{
|
|
||||||
if(figureKeyRef.current)
|
|
||||||
{
|
|
||||||
AVATAR_IMAGE_CACHE.delete(figureKeyRef.current);
|
|
||||||
setUpdateId(prev => prev + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const getClassNames = useMemo(() =>
|
const getClassNames = useMemo(() =>
|
||||||
{
|
{
|
||||||
@@ -65,44 +53,39 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
|||||||
|
|
||||||
const figureKey = [ figure, gender, direction, headOnly ].join('-');
|
const figureKey = [ figure, gender, direction, headOnly ].join('-');
|
||||||
|
|
||||||
figureKeyRef.current = figureKey;
|
|
||||||
|
|
||||||
if(AVATAR_IMAGE_CACHE.has(figureKey))
|
if(AVATAR_IMAGE_CACHE.has(figureKey))
|
||||||
{
|
{
|
||||||
setAvatarUrl(AVATAR_IMAGE_CACHE.get(figureKey));
|
setAvatarUrl(AVATAR_IMAGE_CACHE.get(figureKey));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, {
|
const resetFigure = (_figure: string) =>
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GetRoomEngine, IGetImageListener, ImageResult, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
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 { ProductTypeEnum } from '../../api';
|
||||||
import { Base, BaseProps } from '../Base';
|
import { Base, BaseProps } from '../Base';
|
||||||
|
|
||||||
@@ -16,6 +16,23 @@ export const LayoutFurniImageView: FC<LayoutFurniImageViewProps> = props =>
|
|||||||
{
|
{
|
||||||
const { productType = 's', productClassId = -1, direction = 2, extraData = '', scale = 1, style = {}, ...rest } = props;
|
const { productType = 's', productClassId = -1, direction = 2, extraData = '', scale = 1, style = {}, ...rest } = props;
|
||||||
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
|
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(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(() =>
|
const getStyle = useMemo(() =>
|
||||||
{
|
{
|
||||||
@@ -42,10 +59,12 @@ export const LayoutFurniImageView: FC<LayoutFurniImageViewProps> = props =>
|
|||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
|
setImageElement(null);
|
||||||
|
|
||||||
let imageResult: ImageResult = null;
|
let imageResult: ImageResult = null;
|
||||||
|
|
||||||
const listener: IGetImageListener = {
|
const listener: IGetImageListener = {
|
||||||
imageReady: async (id, texture, image) => setImageElement(await TextureUtils.generateImage(texture)),
|
imageReady: (result) => updateImage(result?.data),
|
||||||
imageFailed: null
|
imageFailed: null
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,12 +78,8 @@ export const LayoutFurniImageView: FC<LayoutFurniImageViewProps> = props =>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!imageResult) return;
|
if(imageResult?.data) updateImage(imageResult.data);
|
||||||
|
}, [ productType, productClassId, direction, extraData, updateImage ]);
|
||||||
(async () => setImageElement(await TextureUtils.generateImage(imageResult.data)))();
|
|
||||||
}, [ productType, productClassId, direction, extraData ]);
|
|
||||||
|
|
||||||
if(!imageElement) return null;
|
|
||||||
|
|
||||||
return <Base classNames={ [ 'furni-image' ] } style={ getStyle } { ...rest } />;
|
return <Base classNames={ [ 'furni-image' ] } style={ getStyle } { ...rest } />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,15 +24,13 @@ export const LayoutRoomPreviewerView: FC<{
|
|||||||
const width = elementRef.current.parentElement.clientWidth;
|
const width = elementRef.current.parentElement.clientWidth;
|
||||||
const texture = TextureUtils.createRenderTexture(width, height);
|
const texture = TextureUtils.createRenderTexture(width, height);
|
||||||
|
|
||||||
const update = async (ticker: NitroTicker) =>
|
const paintToDOM = () =>
|
||||||
{
|
{
|
||||||
if(!roomPreviewer || !elementRef.current) return;
|
if(!roomPreviewer || !elementRef.current) return;
|
||||||
|
|
||||||
roomPreviewer.updatePreviewRoomView();
|
|
||||||
|
|
||||||
const renderingCanvas = roomPreviewer.getRenderingCanvas();
|
const renderingCanvas = roomPreviewer.getRenderingCanvas();
|
||||||
|
|
||||||
if(!renderingCanvas.canvasUpdated) return;
|
if(!renderingCanvas) return;
|
||||||
|
|
||||||
GetRenderer().render({
|
GetRenderer().render({
|
||||||
target: texture,
|
target: texture,
|
||||||
@@ -48,6 +46,20 @@ export const LayoutRoomPreviewerView: FC<{
|
|||||||
elementRef.current.style.backgroundImage = `url(${ base64 })`;
|
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);
|
GetTicker().add(update);
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(() =>
|
const resizeObserver = new ResizeObserver(() =>
|
||||||
@@ -58,7 +70,7 @@ export const LayoutRoomPreviewerView: FC<{
|
|||||||
|
|
||||||
roomPreviewer.modifyRoomCanvas(width, height);
|
roomPreviewer.modifyRoomCanvas(width, height);
|
||||||
|
|
||||||
update(GetTicker());
|
paintToDOM();
|
||||||
});
|
});
|
||||||
|
|
||||||
roomPreviewer.getRoomCanvas(width, height);
|
roomPreviewer.getRoomCanvas(width, height);
|
||||||
|
|||||||
+5
-1
@@ -45,7 +45,11 @@ export const NotificationBadgeReceivedBubbleView: FC<NotificationBadgeReceivedBu
|
|||||||
{ item.iconUrl && <img alt="" className="no-select" src={ item.iconUrl } /> }
|
{ item.iconUrl && <img alt="" className="no-select" src={ item.iconUrl } /> }
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex column gap={ 0 }>
|
<Flex column gap={ 0 }>
|
||||||
<Text bold variant="white">{ LocalizeText('notification.badge.received') }</Text>
|
<Text bold variant="white">
|
||||||
|
{ item.senderName
|
||||||
|
? LocalizeText('notifications.text.received.badge', [ 'user_name' ], [ item.senderName ])
|
||||||
|
: LocalizeText('prereg.reward.you.received') }
|
||||||
|
</Text>
|
||||||
<Text variant="white" small>{ item.message }</Text>
|
<Text variant="white" small>{ item.message }</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -2,6 +2,33 @@
|
|||||||
|
|
||||||
@config "../../tailwind.config.js";
|
@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-face {
|
||||||
font-family: Ubuntu;
|
font-family: Ubuntu;
|
||||||
src: url("@/assets/webfonts/Ubuntu-C.ttf");
|
src: url("@/assets/webfonts/Ubuntu-C.ttf");
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ const useNotificationState = () =>
|
|||||||
|
|
||||||
const showNitroAlert = useCallback(() => simpleAlert(null, NotificationAlertType.NITRO), [ simpleAlert ]);
|
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;
|
if(bubblesDisabled) return;
|
||||||
|
|
||||||
const notificationItem = new NotificationBubbleItem(message, type, imageUrl, internalLink);
|
const notificationItem = new NotificationBubbleItem(message, type, imageUrl, internalLink, senderName);
|
||||||
|
|
||||||
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
|
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
|
||||||
}, [ bubblesDisabled ]);
|
}, [ bubblesDisabled ]);
|
||||||
@@ -226,7 +226,6 @@ const useNotificationState = () =>
|
|||||||
{
|
{
|
||||||
const parser = event.getParser();
|
const parser = event.getParser();
|
||||||
|
|
||||||
// Skip if AchievementNotificationMessageEvent already showed a notification for this badge
|
|
||||||
if(recentBadgeNotifications.has(parser.badgeCode)) return;
|
if(recentBadgeNotifications.has(parser.badgeCode)) return;
|
||||||
|
|
||||||
recentBadgeNotifications.add(parser.badgeCode);
|
recentBadgeNotifications.add(parser.badgeCode);
|
||||||
@@ -234,8 +233,9 @@ const useNotificationState = () =>
|
|||||||
|
|
||||||
const badgeName = LocalizeBadgeName(parser.badgeCode);
|
const badgeName = LocalizeBadgeName(parser.badgeCode);
|
||||||
const badgeImage = GetSessionDataManager().getBadgeUrl(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>(ClubGiftNotificationEvent, event =>
|
useMessageEvent<ClubGiftNotificationEvent>(ClubGiftNotificationEvent, event =>
|
||||||
|
|||||||
@@ -102,30 +102,6 @@ module.exports = {
|
|||||||
dropShadow: {
|
dropShadow: {
|
||||||
'hover': '2px 2px 0 rgba(0,0,0,0.8)'
|
'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: [
|
safelist: [
|
||||||
|
|||||||
Reference in New Issue
Block a user