WIP preserve local changes before duckie merge

This commit is contained in:
Lorenzune
2026-04-21 11:13:32 +02:00
parent e0174e450c
commit 9b36513def
74 changed files with 4419 additions and 408 deletions
@@ -382,6 +382,23 @@ const useAvatarInfoWidgetState = () =>
return () => clearPendingAvatarInfo();
}, []);
useEffect(() =>
{
const refreshFurnitureInfo = () =>
{
setAvatarInfo(prevValue =>
{
if(!(prevValue instanceof AvatarInfoFurni)) return prevValue;
return AvatarInfoUtilities.getFurniInfo(prevValue.id, prevValue.category) || prevValue;
});
};
window.addEventListener('nitro-localization-updated', refreshFurnitureInfo);
return () => window.removeEventListener('nitro-localization-updated', refreshFurnitureInfo);
}, []);
useEffect(() =>
{
if(!roomSession) return;
+47 -10
View File
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
import { ChatMessageTypeEnum, GetClubMemberLevel, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../api';
import { useNitroEvent } from '../../events';
import { useNotification } from '../../notification';
import { useTranslation } from '../../translation';
import { useObjectSelectedEvent } from '../engine';
import { useRoom } from '../useRoom';
@@ -15,6 +16,7 @@ const useChatInputWidgetState = () =>
const [ floodBlocked, setFloodBlocked ] = useState(false);
const [ floodBlockedSeconds, setFloodBlockedSeconds ] = useState(0);
const { showNitroAlert = null, showConfirm = null } = useNotification();
const { settings, translateOutgoing, enqueueOutgoingTranslation } = useTranslation();
const { roomSession = null } = useRoom();
const sendChat = (text: string, chatType: number, recipientName: string = '', styleId: number = 0) =>
@@ -183,22 +185,57 @@ const useChatInputWidgetState = () =>
SendMessageComposer(new RoomSettingsComposer(roomSession.roomId));
}
return null;
case ':customize':
CreateLinkEvent('customize/show');
return null;
}
}
switch(chatType)
const preserveTrailingSpaces = (message: string) => message.replace(/ +$/g, match => '\u00A0'.repeat(match.length));
const dispatchChatMessage = (message: string) =>
{
case ChatMessageTypeEnum.CHAT_DEFAULT:
roomSession.sendChatMessage(text, styleId);
break;
case ChatMessageTypeEnum.CHAT_SHOUT:
roomSession.sendShoutMessage(text, styleId);
break;
case ChatMessageTypeEnum.CHAT_WHISPER:
roomSession.sendWhisperMessage(recipientName, text, styleId);
break;
const preservedMessage = preserveTrailingSpaces(message);
switch(chatType)
{
case ChatMessageTypeEnum.CHAT_DEFAULT:
roomSession.sendChatMessage(preservedMessage, styleId);
return;
case ChatMessageTypeEnum.CHAT_SHOUT:
roomSession.sendShoutMessage(preservedMessage, styleId);
return;
case ChatMessageTypeEnum.CHAT_WHISPER:
roomSession.sendWhisperMessage(recipientName, preservedMessage, styleId);
return;
}
};
const trimmedText = text.trimStart();
const shouldTranslateOutgoing = settings.enabled && !!trimmedText.length && (trimmedText.charAt(0) !== ':');
if(!shouldTranslateOutgoing)
{
dispatchChatMessage(text);
return null;
}
void (async () =>
{
const translation = await translateOutgoing(text);
if(translation)
{
enqueueOutgoingTranslation(translation);
dispatchChatMessage(translation.translatedText);
return;
}
dispatchChatMessage(text);
})();
return null;
};
useNitroEvent<RoomSessionChatEvent>(RoomSessionChatEvent.FLOOD_EVENT, event =>
+92 -6
View File
@@ -1,7 +1,8 @@
import { GetGuestRoomResultEvent, GetRoomEngine, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum } from '@nitrots/nitro-renderer';
import { useEffect, useMemo, useRef, useState } from 'react';
import { GetGuestRoomResultEvent, GetRoomEngine, GetSessionDataManager, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum } from '@nitrots/nitro-renderer';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ChatBubbleMessage, ChatBubbleUtilities, ChatEntryType, ChatHistoryCurrentDate, GetConfigurationValue, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api';
import { useMessageEvent, useNitroEvent } from '../../events';
import { useTranslation } from '../../translation';
import { useRoom } from '../useRoom';
import { useChatHistory } from './../../chat-history';
@@ -18,8 +19,58 @@ const useChatWidgetState = () =>
protection: RoomChatSettings.FLOOD_FILTER_NORMAL
});
const { roomSession = null } = useRoom();
const { addChatEntry } = useChatHistory();
const { addChatEntry, updateChatEntry } = useChatHistory();
const { settings, translateIncoming, consumeOutgoingTranslation } = useTranslation();
const isDisposed = useRef(false);
const ownUserId = (GetSessionDataManager()?.userId || -1);
const applyTranslationToBubble = useCallback((chatMessage: ChatBubbleMessage, originalText: string, translatedText: string, detectedLanguage: string, targetLanguage: string) =>
{
const resolvedOriginalText = (originalText || chatMessage.text || '');
const resolvedTranslatedText = (translatedText || resolvedOriginalText);
const originalFormattedText = RoomChatFormatter(resolvedOriginalText);
const translatedFormattedText = RoomChatFormatter(resolvedTranslatedText);
chatMessage.text = resolvedOriginalText;
chatMessage.formattedText = originalFormattedText;
chatMessage.originalText = resolvedOriginalText;
chatMessage.originalFormattedText = originalFormattedText;
chatMessage.translatedText = resolvedTranslatedText;
chatMessage.translatedFormattedText = translatedFormattedText;
chatMessage.translationDetectedLanguage = detectedLanguage || '';
chatMessage.translationTargetLanguage = targetLanguage || '';
chatMessage.showTranslation = true;
}, []);
const buildTranslatedEntryPatch = useCallback((originalText: string, translatedText: string, detectedLanguage: string, targetLanguage: string) =>
{
const resolvedOriginalText = (originalText || '');
const resolvedTranslatedText = (translatedText || resolvedOriginalText);
return {
showTranslation: true,
message: RoomChatFormatter(resolvedOriginalText),
originalMessage: RoomChatFormatter(resolvedOriginalText),
translatedMessage: RoomChatFormatter(resolvedTranslatedText),
detectedLanguage: detectedLanguage || '',
targetLanguage: targetLanguage || ''
};
}, []);
const applyAsyncTranslation = useCallback((bubbleId: number, chatEntryId: number, originalText: string, translatedText: string, detectedLanguage: string, targetLanguage: string) =>
{
setChatMessages(prevValue =>
{
const newValue = [ ...prevValue ];
const bubble = newValue.find(chat => (chat.id === bubbleId));
if(bubble) applyTranslationToBubble(bubble, originalText, translatedText, detectedLanguage, targetLanguage);
return newValue;
});
updateChatEntry(chatEntryId, buildTranslatedEntryPatch(originalText, translatedText, detectedLanguage, targetLanguage));
}, [ applyTranslationToBubble, buildTranslatedEntryPatch, updateChatEntry ]);
const getScrollSpeed = useMemo(() =>
{
@@ -133,14 +184,17 @@ const useChatWidgetState = () =>
}
}
const formattedText = RoomChatFormatter(text);
const isTranslatableChatType = ((chatType === RoomSessionChatEvent.CHAT_TYPE_SPEAK) || (chatType === RoomSessionChatEvent.CHAT_TYPE_WHISPER) || (chatType === RoomSessionChatEvent.CHAT_TYPE_SHOUT));
const outgoingTranslation = (isTranslatableChatType && (userData.webID === ownUserId)) ? consumeOutgoingTranslation(text) : null;
const originalText = outgoingTranslation?.originalText || text;
const formattedText = RoomChatFormatter(originalText);
const color = (avatarColor && (('#' + (avatarColor.toString(16).padStart(6, '0'))) || null));
const chatMessage = new ChatBubbleMessage(
userData.roomIndex,
RoomObjectCategory.UNIT,
roomSession.roomId,
text,
originalText,
formattedText,
username,
{ x: bubbleLocation.x, y: bubbleLocation.y },
@@ -149,10 +203,18 @@ const useChatWidgetState = () =>
imageUrl,
color);
if(outgoingTranslation)
{
applyTranslationToBubble(chatMessage, outgoingTranslation.originalText, outgoingTranslation.translatedText, outgoingTranslation.detectedLanguage, outgoingTranslation.targetLanguage);
}
chatMessage.prefixText = event.prefixText || '';
chatMessage.prefixColor = event.prefixColor || '';
chatMessage.prefixIcon = event.prefixIcon || '';
chatMessage.prefixEffect = event.prefixEffect || '';
chatMessage.prefixFont = event.prefixFont || '';
chatMessage.nickIcon = event.nickIcon || '';
chatMessage.displayOrder = event.displayOrder || 'icon-prefix-name';
setChatMessages(prevValue =>
{
@@ -162,7 +224,31 @@ const useChatWidgetState = () =>
return newValue;
});
addChatEntry({ id: -1, webId: userData.webID, entityId: userData.roomIndex, name: username, imageUrl, style: styleId, chatType: chatType, entityType: userData.type, message: formattedText, timestamp: ChatHistoryCurrentDate(), type: ChatEntryType.TYPE_CHAT, roomId: roomSession.roomId, color });
const chatEntryId = addChatEntry({
id: -1,
webId: userData.webID,
entityId: userData.roomIndex,
name: username,
imageUrl,
style: styleId,
chatType: chatType,
entityType: userData.type,
message: formattedText,
timestamp: ChatHistoryCurrentDate(),
type: ChatEntryType.TYPE_CHAT,
roomId: roomSession.roomId,
color,
...(outgoingTranslation ? buildTranslatedEntryPatch(outgoingTranslation.originalText, outgoingTranslation.translatedText, outgoingTranslation.detectedLanguage, outgoingTranslation.targetLanguage) : {})
});
if(!settings.enabled || outgoingTranslation || !isTranslatableChatType || !text.trim().length) return;
void translateIncoming(text).then(translation =>
{
if(!translation || isDisposed.current) return;
applyAsyncTranslation(chatMessage.id, chatEntryId, translation.originalText, translation.translatedText, translation.detectedLanguage, translation.targetLanguage);
});
});
useNitroEvent<RoomDragEvent>(RoomDragEvent.ROOM_DRAG, event =>