diff --git a/src/api/avatar/AvatarEditorThumbnailsHelper.ts b/src/api/avatar/AvatarEditorThumbnailsHelper.ts index bffc418..f40483d 100644 --- a/src/api/avatar/AvatarEditorThumbnailsHelper.ts +++ b/src/api/avatar/AvatarEditorThumbnailsHelper.ts @@ -150,34 +150,39 @@ export class AvatarEditorThumbnailsHelper if(cached) return cached; - return new Promise(async (resolve, reject) => + const avatarImage = GetAvatarRenderManager().createAvatarImage(figureString, AvatarScaleType.LARGE, null, null); + + if(avatarImage.isPlaceholder()) { - const resetFigure = async (figure: string) => - { - const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false }); + avatarImage.dispose(); - if(avatarImage.isPlaceholder()) return; + return null; + } - const texture = avatarImage.processAsTexture(AvatarSetType.HEAD, false); - const sprite = new NitroSprite(texture); + const texture = avatarImage.processAsTexture(AvatarSetType.HEAD, false); - if(isDisabled) sprite.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ]; + if(!texture) + { + avatarImage.dispose(); - const imageUrl = await TextureUtils.generateImageUrl({ - target: sprite, - frame: new NitroRectangle(0, 0, texture.width, texture.height) - }); + return null; + } - sprite.destroy(); - avatarImage.dispose(); + const sprite = new NitroSprite(texture); - AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl); + if(isDisabled) sprite.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ]; - resolve(imageUrl); - }; - - resetFigure(figureString); + const imageUrl = await TextureUtils.generateImageUrl({ + target: sprite, + frame: new NitroRectangle(0, 0, texture.width, texture.height) }); + + sprite.destroy(); + avatarImage.dispose(); + + if(imageUrl) AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl); + + return imageUrl; } private static sortByDrawOrder(a: IFigurePart, b: IFigurePart): number diff --git a/src/common/layout/LayoutAvatarImageView.tsx b/src/common/layout/LayoutAvatarImageView.tsx index 2b3b156..89779bb 100644 --- a/src/common/layout/LayoutAvatarImageView.tsx +++ b/src/common/layout/LayoutAvatarImageView.tsx @@ -1,6 +1,7 @@ -import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager } from '@nitrots/nitro-renderer'; +import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager, NitroEventType } 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(); @@ -18,7 +19,14 @@ 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 isPlaceholderRef = useRef(false); + + useNitroEvent(NitroEventType.AVATAR_ASSET_LOADED, () => + { + if(isPlaceholderRef.current) setUpdateId(prev => prev + 1); + }); const getClassNames = useMemo(() => { @@ -55,37 +63,49 @@ export const LayoutAvatarImageView: FC = props => if(AVATAR_IMAGE_CACHE.has(figureKey)) { + isPlaceholderRef.current = false; setAvatarUrl(AVATAR_IMAGE_CACHE.get(figureKey)); } else { - const resetFigure = (_figure: string) => - { - if(isDisposed.current) return; - - const avatarImage = GetAvatarRenderManager().createAvatarImage(_figure, AvatarScaleType.LARGE, gender, { resetFigure: (figure: string) => resetFigure(figure), 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 avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, { + resetFigure: (figure: string) => { - if(!avatarImage.isPlaceholder()) AVATAR_IMAGE_CACHE.set(figureKey, imageUrl); + if(isDisposed.current) return; - setAvatarUrl(imageUrl); + isPlaceholderRef.current = false; + 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); + isPlaceholderRef.current = false; + } + else + { + isPlaceholderRef.current = true; } - avatarImage.dispose(); - }; + setAvatarUrl(imageUrl); + } - resetFigure(figure); + avatarImage.dispose(); } - }, [ figure, gender, direction, headOnly, isReady ]); + }, [ figure, gender, direction, headOnly, isReady, updateId ]); useEffect(() => { diff --git a/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx index 789dc74..588005e 100644 --- a/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx +++ b/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx @@ -1,7 +1,9 @@ +import { NitroEventType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { AvatarEditorThumbnailsHelper, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../api'; import { LayoutCurrencyIcon, LayoutGridItemProps } from '../../../common'; import { useAvatarEditor } from '../../../hooks'; +import { useNitroEvent } from '../../../hooks/events'; import { InfiniteGrid } from '../../../layout'; import { AvatarEditorIcon } from '../AvatarEditorIcon'; @@ -14,6 +16,7 @@ export const AvatarEditorFigureSetItemView: FC<{ { const { setType = null, partItem = null, isSelected = false, width = '100%', ...rest } = props; const [ assetUrl, setAssetUrl ] = useState(''); + const [ retryId, setRetryId ] = useState(0); const { selectedColorParts = null, getFigureStringWithFace = null } = useAvatarEditor(); const clubLevel = partItem.partSet?.clubLevel ?? 0; @@ -21,6 +24,11 @@ export const AvatarEditorFigureSetItemView: FC<{ const isLocked = isHC && (GetClubMemberLevel() < clubLevel); const isSellableNotOwned = partItem.isSellableNotOwned ?? false; + useNitroEvent(NitroEventType.AVATAR_ASSET_LOADED, () => + { + if(!assetUrl || !assetUrl.length) setRetryId(prev => prev + 1); + }); + useEffect(() => { setAssetUrl(''); @@ -54,7 +62,7 @@ export const AvatarEditorFigureSetItemView: FC<{ }; loadImage(); - }, [ setType, partItem, selectedColorParts, getFigureStringWithFace, isSellableNotOwned ]); + }, [ setType, partItem, selectedColorParts, getFigureStringWithFace, isSellableNotOwned, retryId ]); if(!partItem) return null; diff --git a/src/components/user-profile/UserContainerView.tsx b/src/components/user-profile/UserContainerView.tsx index a6b3a00..88d3150 100644 --- a/src/components/user-profile/UserContainerView.tsx +++ b/src/components/user-profile/UserContainerView.tsx @@ -8,8 +8,11 @@ export const UserContainerView: FC<{ }> = props => { const { userProfile = null } = props; + const [ requestSent, setRequestSent ] = useState(userProfile.requestSent); + const isOwnProfile = (userProfile.id === GetSessionDataManager().userId); + const canSendFriendRequest = !requestSent && (!isOwnProfile && !userProfile.isMyFriend && !userProfile.requestSent); const infostandBackgroundClass = `background-${userProfile.backgroundId ?? 'default'}`; @@ -35,36 +38,75 @@ export const UserContainerView: FC<{
+

{ userProfile.username }

{ userProfile.motto }

+
-

-

-

- { LocalizeText('extendedprofile.friends.count') } { userProfile.friendsCount } -

+

+ +

+ +

+

{ LocalizeText('extendedprofile.achievementscore') } { userProfile.achievementPoints }

+
{ userProfile.isOnline && } + { !userProfile.isOnline && } +
{ canSendFriendRequest && - { LocalizeText('extendedprofile.addasafriend') } } + + { LocalizeText('extendedprofile.addasafriend') } + } + { !canSendFriendRequest && <> + { isOwnProfile &&

{ LocalizeText('extendedprofile.me') }

} + { userProfile.isMyFriend &&

{ LocalizeText('extendedprofile.friend') }

} + { (requestSent || userProfile.requestSent) &&

{ LocalizeText('extendedprofile.friendrequestsent') }

} } @@ -73,4 +115,4 @@ export const UserContainerView: FC<{
); -}; +}; \ No newline at end of file diff --git a/src/components/user-profile/UserProfileView.tsx b/src/components/user-profile/UserProfileView.tsx index b294346..67bb6b4 100644 --- a/src/components/user-profile/UserProfileView.tsx +++ b/src/components/user-profile/UserProfileView.tsx @@ -9,7 +9,7 @@ import { FriendsContainerView } from './FriendsContainerView'; import { GroupsContainerView } from './GroupsContainerView'; import { UserContainerView } from './UserContainerView'; -type ProfileTab = 'badge' | 'amici' | 'stanze' | 'gruppi'; +type ProfileTab = 'badge' | 'friends' | 'rooms' | 'groups'; export const UserProfileView: FC<{}> = props => { @@ -39,7 +39,7 @@ export const UserProfileView: FC<{}> = props => { setActiveTab(tab); - if(tab === 'stanze' && !userRooms && userProfile) + if(tab === 'rooms' && !userRooms && userProfile) { SendMessageComposer(new NavigatorSearchComposer('hotel_view', `owner:${ userProfile.username }`)); } @@ -99,7 +99,7 @@ export const UserProfileView: FC<{}> = props => useMessageEvent(NavigatorSearchEvent, event => { - if(!userProfile || activeTab !== 'stanze') return; + if(!userProfile || activeTab !== 'rooms') return; const parser = event.getParser(); const result = parser.result; @@ -145,16 +145,16 @@ export const UserProfileView: FC<{}> = props =>
onTabClick('badge') }> - { LocalizeText('extendedprofile.tab.badge') } + { LocalizeText('levelinfo.category.badge') } - onTabClick('amici') }> - { LocalizeText('extendedprofile.tab.friends') } + onTabClick('friends') }> + { LocalizeText('navigator.tab.3') } - onTabClick('stanze') }> - { LocalizeText('extendedprofile.tab.rooms') } + onTabClick('rooms') }> + { LocalizeText('navigator.tab.2') } - onTabClick('gruppi') }> - { LocalizeText('extendedprofile.tab.groups') } + onTabClick('groups') }> + { LocalizeText('navigator.searchcode.title.groups') }
@@ -172,7 +172,7 @@ export const UserProfileView: FC<{}> = props => }
) } - { activeTab === 'amici' && ( + { activeTab === 'friends' && (
{ userRelationships ? ( @@ -183,7 +183,7 @@ export const UserProfileView: FC<{}> = props => ) }
) } - { activeTab === 'stanze' && ( + { activeTab === 'rooms' && (
{ !userRooms && ( @@ -206,7 +206,7 @@ export const UserProfileView: FC<{}> = props => )) }
) } - { activeTab === 'gruppi' && ( + { activeTab === 'groups' && (