mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
Merge upstream/main into feature/checkpoint-20260403
This commit is contained in:
@@ -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<string>('');
|
||||
const [ retryId, setRetryId ] = useState<number>(0);
|
||||
const { selectedColorParts = null, getFigureStringWithFace = null } = useAvatarEditor();
|
||||
|
||||
const clubLevel = partItem.partSet?.clubLevel ?? 0;
|
||||
@@ -21,6 +24,15 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
const isLocked = isHC && (GetClubMemberLevel() < clubLevel);
|
||||
const isSellableNotOwned = partItem.isSellableNotOwned ?? false;
|
||||
|
||||
useNitroEvent(NitroEventType.AVATAR_ASSET_LOADED, () =>
|
||||
{
|
||||
if(!assetUrl || !assetUrl.length)
|
||||
{
|
||||
AvatarEditorThumbnailsHelper.clearCache();
|
||||
setRetryId(prev => prev + 1);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setAssetUrl('');
|
||||
@@ -54,7 +66,7 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
};
|
||||
|
||||
loadImage();
|
||||
}, [ setType, partItem, selectedColorParts, getFigureStringWithFace, isSellableNotOwned ]);
|
||||
}, [ setType, partItem, selectedColorParts, getFigureStringWithFace, isSellableNotOwned, retryId ]);
|
||||
|
||||
if(!partItem) return null;
|
||||
|
||||
|
||||
@@ -18,16 +18,18 @@ export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props =>
|
||||
const { requestOfferToMover = null } = useCatalog();
|
||||
const { isVisible = false } = useInventoryFurni();
|
||||
const { isFavoriteOffer, toggleFavoriteOffer } = useCatalogFavorites();
|
||||
const isFav = isFavoriteOffer(offer.offerId);
|
||||
const isFav = offer ? isFavoriteOffer(offer.offerId) : false;
|
||||
|
||||
const iconUrl = useMemo(() =>
|
||||
{
|
||||
if(!offer) return null;
|
||||
|
||||
if(offer.pricingModel === Offer.PRICING_MODEL_BUNDLE)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return offer.product.getIconUrl(offer);
|
||||
return offer.product?.getIconUrl(offer) ?? null;
|
||||
}, [ offer ]);
|
||||
|
||||
const onMouseEvent = (event: MouseEvent) =>
|
||||
@@ -49,6 +51,8 @@ export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props =>
|
||||
}
|
||||
};
|
||||
|
||||
if(!offer) return null;
|
||||
|
||||
const product = offer.product;
|
||||
|
||||
if(!product) return null;
|
||||
|
||||
@@ -22,6 +22,8 @@ export const CatalogSearchView: FC<{}> = () =>
|
||||
|
||||
const timeout = setTimeout(() =>
|
||||
{
|
||||
if(!offersToNodes || !rootNode) return;
|
||||
|
||||
const furnitureDatas = GetSessionDataManager().getAllFurnitureData();
|
||||
|
||||
if(!furnitureDatas || !furnitureDatas.length) return;
|
||||
@@ -31,11 +33,13 @@ export const CatalogSearchView: FC<{}> = () =>
|
||||
|
||||
for(const furniture of furnitureDatas)
|
||||
{
|
||||
if(!furniture) continue;
|
||||
|
||||
if((currentType === CatalogType.BUILDER) && !furniture.availableForBuildersClub) continue;
|
||||
|
||||
if((currentType === CatalogType.NORMAL) && furniture.excludeDynamic) continue;
|
||||
|
||||
const searchValues = [ furniture.className, furniture.name, furniture.description ].join(' ').replace(/ /gi, '').toLowerCase();
|
||||
const searchValues = [ furniture.className || '', furniture.name || '', furniture.description || '' ].join(' ').replace(/ /gi, '').toLowerCase();
|
||||
|
||||
if((currentType === CatalogType.BUILDER) && (furniture.purchaseOfferId === -1) && (furniture.rentOfferId === -1))
|
||||
{
|
||||
|
||||
@@ -17,42 +17,48 @@ export const FurnitureYoutubeDisplayView: FC<{}> = FurnitureYoutubeDisplayViewPr
|
||||
|
||||
const onStateChange = (event: { target: YouTubePlayer; data: number }) =>
|
||||
{
|
||||
setPlayer(event.target);
|
||||
|
||||
if(objectId === -1) return;
|
||||
|
||||
switch(event.target.getPlayerState())
|
||||
try
|
||||
{
|
||||
case -1:
|
||||
case 1:
|
||||
if(currentVideoState === 2)
|
||||
{
|
||||
//event.target.pauseVideo();
|
||||
}
|
||||
setPlayer(event.target);
|
||||
|
||||
if(currentVideoState !== 1) play();
|
||||
return;
|
||||
case 2:
|
||||
if(currentVideoState !== 2) pause();
|
||||
if(objectId === -1) return;
|
||||
|
||||
switch(event.target.getPlayerState())
|
||||
{
|
||||
case -1:
|
||||
case 1:
|
||||
if(currentVideoState !== 1) play();
|
||||
return;
|
||||
case 2:
|
||||
if(currentVideoState !== 2) pause();
|
||||
}
|
||||
}
|
||||
catch(err) {}
|
||||
};
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if((currentVideoState === null) || !player) return;
|
||||
|
||||
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PLAYING) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PLAYING))
|
||||
try
|
||||
{
|
||||
player.playVideo();
|
||||
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PLAYING) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PLAYING))
|
||||
{
|
||||
player.playVideo();
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PAUSED) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PAUSED))
|
||||
{
|
||||
player.pauseVideo();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PAUSED) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PAUSED))
|
||||
catch(err)
|
||||
{
|
||||
player.pauseVideo();
|
||||
|
||||
return;
|
||||
setPlayer(null);
|
||||
}
|
||||
}, [ currentVideoState, player ]);
|
||||
|
||||
|
||||
@@ -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<{
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ userProfile.figure } />
|
||||
<div className={`absolute inset-0 profile-overlay ${infostandOverlayClass}`} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-0">
|
||||
<p className="leading-tight font-bold">{ userProfile.username }</p>
|
||||
<p className="text-sm italic leading-tight">{ userProfile.motto }</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm leading-none" dangerouslySetInnerHTML={{ __html: LocalizeText('extendedprofile.created', ['created'], [userProfile.registration]) }} />
|
||||
<p className="text-sm leading-none" dangerouslySetInnerHTML={{ __html: LocalizeText('extendedprofile.last.login', ['lastlogin'], [FriendlyTime.format(userProfile.secondsSinceLastVisit, '.ago', 2)]) }} />
|
||||
<p className="text-sm leading-none">
|
||||
<b>{ LocalizeText('extendedprofile.friends.count') }</b> { userProfile.friendsCount }
|
||||
</p>
|
||||
<p
|
||||
className="text-sm leading-none"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: LocalizeText(
|
||||
'extendedprofile.created',
|
||||
['created'],
|
||||
[userProfile.registration]
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<p
|
||||
className="text-sm leading-none"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: LocalizeText(
|
||||
'extendedprofile.last.login',
|
||||
['lastlogin'],
|
||||
[FriendlyTime.format(userProfile.secondsSinceLastVisit, '.ago', 2)]
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<p
|
||||
className="text-sm leading-none"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: LocalizeText(
|
||||
'extendedprofile.friends.count',
|
||||
['count'],
|
||||
[userProfile.friendsCount]
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<p className="text-sm leading-none">
|
||||
<b>{ LocalizeText('extendedprofile.achievementscore') }</b> { userProfile.achievementPoints }
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{ userProfile.isOnline &&
|
||||
<i className="nitro-icon icon-pf-online" /> }
|
||||
|
||||
{ !userProfile.isOnline &&
|
||||
<i className="nitro-icon icon-pf-offline" /> }
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{ canSendFriendRequest &&
|
||||
<Text pointer small underline onClick={ addFriend }>{ LocalizeText('extendedprofile.addasafriend') }</Text> }
|
||||
<Text pointer small underline onClick={ addFriend }>
|
||||
{ LocalizeText('extendedprofile.addasafriend') }
|
||||
</Text> }
|
||||
|
||||
{ !canSendFriendRequest &&
|
||||
<>
|
||||
<i className="nitro-icon icon-pf-tick" />
|
||||
|
||||
{ isOwnProfile &&
|
||||
<p>{ LocalizeText('extendedprofile.me') }</p> }
|
||||
|
||||
{ userProfile.isMyFriend &&
|
||||
<p>{ LocalizeText('extendedprofile.friend') }</p> }
|
||||
|
||||
{ (requestSent || userProfile.requestSent) &&
|
||||
<p>{ LocalizeText('extendedprofile.friendrequestsent') }</p> }
|
||||
</> }
|
||||
@@ -73,4 +115,4 @@ export const UserContainerView: FC<{
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -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>(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 =>
|
||||
</div>
|
||||
<NitroCard.Tabs>
|
||||
<NitroCard.TabItem isActive={ activeTab === 'badge' } count={ userBadges.length } onClick={ () => onTabClick('badge') }>
|
||||
{ LocalizeText('extendedprofile.tab.badge') }
|
||||
{ LocalizeText('levelinfo.category.badge') }
|
||||
</NitroCard.TabItem>
|
||||
<NitroCard.TabItem isActive={ activeTab === 'amici' } count={ userProfile.friendsCount } onClick={ () => onTabClick('amici') }>
|
||||
{ LocalizeText('extendedprofile.tab.friends') }
|
||||
<NitroCard.TabItem isActive={ activeTab === 'friends' } count={ userProfile.friendsCount } onClick={ () => onTabClick('friends') }>
|
||||
{ LocalizeText('navigator.tab.3') }
|
||||
</NitroCard.TabItem>
|
||||
<NitroCard.TabItem isActive={ activeTab === 'stanze' } onClick={ () => onTabClick('stanze') }>
|
||||
{ LocalizeText('extendedprofile.tab.rooms') }
|
||||
<NitroCard.TabItem isActive={ activeTab === 'rooms' } onClick={ () => onTabClick('rooms') }>
|
||||
{ LocalizeText('navigator.tab.2') }
|
||||
</NitroCard.TabItem>
|
||||
<NitroCard.TabItem isActive={ activeTab === 'gruppi' } count={ userProfile.groups?.length } onClick={ () => onTabClick('gruppi') }>
|
||||
{ LocalizeText('extendedprofile.tab.groups') }
|
||||
<NitroCard.TabItem isActive={ activeTab === 'groups' } count={ userProfile.groups?.length } onClick={ () => onTabClick('groups') }>
|
||||
{ LocalizeText('navigator.searchcode.title.groups') }
|
||||
</NitroCard.TabItem>
|
||||
</NitroCard.Tabs>
|
||||
<div className="flex-1 overflow-auto p-2">
|
||||
@@ -172,7 +172,7 @@ export const UserProfileView: FC<{}> = props =>
|
||||
}
|
||||
</div>
|
||||
) }
|
||||
{ activeTab === 'amici' && (
|
||||
{ activeTab === 'friends' && (
|
||||
<div className="flex flex-col gap-2 h-full">
|
||||
{ userRelationships ? (
|
||||
<FriendsContainerView friendsCount={ userProfile.friendsCount } relationships={ userRelationships } />
|
||||
@@ -183,7 +183,7 @@ export const UserProfileView: FC<{}> = props =>
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
{ activeTab === 'stanze' && (
|
||||
{ activeTab === 'rooms' && (
|
||||
<div className="flex flex-col gap-1 h-full">
|
||||
{ !userRooms && (
|
||||
<Flex center className="h-full">
|
||||
@@ -206,7 +206,7 @@ export const UserProfileView: FC<{}> = props =>
|
||||
)) }
|
||||
</div>
|
||||
) }
|
||||
{ activeTab === 'gruppi' && (
|
||||
{ activeTab === 'groups' && (
|
||||
<div className="h-full">
|
||||
<GroupsContainerView fullWidth groups={ userProfile.groups } itsMe={ userProfile.id === GetSessionDataManager().userId } onLeaveGroup={ onLeaveGroup } />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user