Update infostand

This commit is contained in:
Bill 2022-03-01 00:09:02 -05:00
parent c7d3022ccb
commit 86d1d8ceba
19 changed files with 601 additions and 498 deletions

View File

@ -0,0 +1,57 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react';
import { LocalizeText, RoomWidgetUpdateInfostandUserEvent } from '../../../../api';
import { Column, Flex, Text } from '../../../../common';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
interface InfoStandWidgetBotViewProps
{
botData: RoomWidgetUpdateInfostandUserEvent;
close: () => void;
}
export const InfoStandWidgetBotView: FC<InfoStandWidgetBotViewProps> = props =>
{
const { botData = null, close = null } = props;
if(!botData) return null;
return (
<Column className="nitro-infostand rounded">
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
<Column gap={ 1 }>
<Flex alignItems="center" justifyContent="between" gap={ 1 }>
<Text variant="white" small wrap>{ botData.name }</Text>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex gap={ 1 }>
<Column fullWidth className="body-image bot">
<AvatarImageView figure={ botData.figure } direction={ 4 } />
</Column>
<Column grow center gap={ 0 }>
{ (botData.badges.length > 0) && botData.badges.map(result =>
{
return <BadgeImageView key={ result } badgeCode={ result } showInfo={ true } />;
}) }
</Column>
</Flex>
<hr className="m-0" />
</Column>
<Flex alignItems="center" className="bg-light-dark rounded py-1 px-2">
<Text fullWidth wrap textBreak variant="white" small className="motto-content">{ botData.motto }</Text>
</Flex>
{ (botData.carryItem > 0) &&
<Column gap={ 1 }>
<hr className="m-0" />
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + botData.carryItem) ]) }
</Text>
</Column> }
</Column>
</Column>
);
}

View File

@ -1,14 +1,20 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CrackableDataType, GroupInformationComposer, GroupInformationEvent, RoomControllerLevel, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnumItemExtradataParameter, RoomWidgetFurniInfoUsagePolicyEnum, SetObjectDataMessageComposer, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../../api';
import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
import { UserProfileIconView } from '../../../../../../layout';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { LimitedEditionCompactPlateView } from '../../../../../shared/limited-edition/LimitedEditionCompactPlateView';
import { RarityLevelView } from '../../../../../shared/rarity-level/RarityLevelView';
import { useRoomContext } from '../../../../context/RoomContext';
import { InfoStandBaseView } from '../base/InfoStandBaseView';
import { InfoStandWidgetFurniViewProps } from './InfoStandWidgetFurniView.types';
import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage, RoomWidgetUpdateInfostandFurniEvent } from '../../../../api';
import { Button, Column, Flex, Text } from '../../../../common';
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
import { UserProfileIconView } from '../../../../layout';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
import { LimitedEditionCompactPlateView } from '../../../shared/limited-edition/LimitedEditionCompactPlateView';
import { RarityLevelView } from '../../../shared/rarity-level/RarityLevelView';
import { useRoomContext } from '../../context/RoomContext';
interface InfoStandWidgetFurniViewProps
{
furniData: RoomWidgetUpdateInfostandFurniEvent;
close: () => void;
}
const PICKUP_MODE_NONE: number = 0;
const PICKUP_MODE_EJECT: number = 1;
@ -117,19 +123,22 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
if(furniData.isStickie) pickupMode = PICKUP_MODE_NONE;
setPickupMode(pickupMode);
setCanMove(canMove);
setCanRotate(canRotate);
setCanUse(canUse);
setFurniKeys(furniKeyss);
setFurniValues(furniValuess);
setCustomKeys(customKeyss);
setCustomValues(customValuess);
setIsCrackable(isCrackable);
setCrackableHits(crackableHits);
setCrackableTarget(crackableTarget);
setGodMode(godMode);
setGroupName(null);
BatchUpdates(() =>
{
setPickupMode(pickupMode);
setCanMove(canMove);
setCanRotate(canRotate);
setCanUse(canUse);
setFurniKeys(furniKeyss);
setFurniValues(furniValuess);
setCustomKeys(customKeyss);
setCustomValues(customValuess);
setIsCrackable(isCrackable);
setCrackableHits(crackableHits);
setCrackableTarget(crackableTarget);
setGodMode(godMode);
setGroupName(null);
});
if(furniData.groupId) SendMessageHook(new GroupInformationComposer(furniData.groupId, false));
}, [ roomSession, furniData ]);
@ -247,97 +256,125 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
if(!furniData) return null;
return (
<>
<InfoStandBaseView headerText={ furniData.name } onCloseClick={ close }>
<div className="position-relative w-100">
{ furniData.stuffData.isUnique &&
<div className="position-absolute end-0">
<LimitedEditionCompactPlateView uniqueNumber={ furniData.stuffData.uniqueNumber } uniqueSeries={ furniData.stuffData.uniqueSeries } />
</div> }
{ (furniData.stuffData.rarityLevel > -1) &&
<div className="position-absolute end-0">
<RarityLevelView level={ furniData.stuffData.rarityLevel } />
</div> }
{ furniData.image && furniData.image.src.length &&
<img className="d-block mx-auto" src={ furniData.image.src } alt="" /> }
</div>
<hr className="m-0 my-1" />
<div className="small text-wrap">{ furniData.description }</div>
<hr className="m-0 my-1" />
<div className="d-flex align-items-center">
<UserProfileIconView userId={ furniData.ownerId } />
<div className="small text-wrap">{ LocalizeText('furni.owner', [ 'name' ], [ furniData.ownerName ]) }</div>
</div>
{ (furniData.purchaseOfferId > 0) && <button type="button" className="btn btn-primary btn-sm mt-1" onClick={ event => processButtonAction('buy_one') }>{ LocalizeText('infostand.button.buy') }</button> }
{ isCrackable &&
<>
<hr className="m-0 my-1" />
<div className="small text-wrap">{ LocalizeText('infostand.crackable_furni.hits_remaining', [ 'hits', 'target' ], [ crackableHits.toString(), crackableTarget.toString() ]) }</div>
</> }
{ furniData.groupId > 0 &&
<>
<hr className="m-0 my-1" />
<div className="d-flex align-items-center cursor-pointer text-decoration-underline gap-2" onClick={ () => GetGroupInformation(furniData.groupId) }>
<BadgeImageView badgeCode={ getGroupBadgeCode() } isGroup={ true } />
<div>{ groupName }</div>
</div>
</> }
{ godMode &&
<>
<hr className="m-0 my-1" />
<div className="small text-wrap">ID: { furniData.id }</div>
{ (furniKeys.length > 0) &&
<Column gap={ 1 } alignItems="end">
<Column className="nitro-infostand rounded">
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
<Column gap={ 1 }>
<Flex alignItems="center" justifyContent="between" gap={ 1 }>
<Text variant="white" small wrap>{ furniData.name }</Text>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex position="relative" gap={ 1 }>
{ furniData.stuffData.isUnique &&
<div className="position-absolute end-0">
<LimitedEditionCompactPlateView uniqueNumber={ furniData.stuffData.uniqueNumber } uniqueSeries={ furniData.stuffData.uniqueSeries } />
</div> }
{ (furniData.stuffData.rarityLevel > -1) &&
<div className="position-absolute end-0">
<RarityLevelView level={ furniData.stuffData.rarityLevel } />
</div> }
{ furniData.image && furniData.image.src.length &&
<img className="d-block mx-auto" src={ furniData.image.src } alt="" /> }
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Text fullWidth wrap textBreak variant="white" small>{ furniData.description }</Text>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex alignItems="center" gap={ 1 }>
<UserProfileIconView userId={ furniData.ownerId } />
<Text variant="white" small wrap>
{ LocalizeText('furni.owner', [ 'name' ], [ furniData.ownerName ]) }
</Text>
</Flex>
{ (furniData.purchaseOfferId > 0) &&
<Flex>
<Text variant="white" small underline pointer onClick={ event => processButtonAction('buy_one') }>
{ LocalizeText('infostand.button.buy') }
</Text>
</Flex> }
</Column>
<Column gap={ 1 }>
{ isCrackable &&
<>
<hr className="m-0" />
<Text variant="white" small wrap>{ LocalizeText('infostand.crackable_furni.hits_remaining', [ 'hits', 'target' ], [ crackableHits.toString(), crackableTarget.toString() ]) }</Text>
</> }
{ furniData.groupId > 0 &&
<>
<hr className="m-0" />
<Flex pointer alignItems="center" gap={ 2 } onClick={ () => GetGroupInformation(furniData.groupId) }>
<BadgeImageView badgeCode={ getGroupBadgeCode() } isGroup={ true } />
<Text variant="white" underline>{ groupName }</Text>
</Flex>
</> }
{ godMode &&
<>
<hr className="m-0" />
<Text small wrap variant="white">ID: { furniData.id }</Text>
{ (furniKeys.length > 0) &&
<>
<hr className="m-0"/>
<Column gap={ 1 }>
{ furniKeys.map((key, index) =>
{
return (
<Flex key={ index } alignItems="center" gap={ 1 }>
<Text small wrap align="end" variant="white" className="col-4">{ key }</Text>
<input type="text" className="form-control form-control-sm" value={ furniValues[index] } onChange={ event => onFurniSettingChange(index, event.target.value) }/>
</Flex>);
}) }
</Column>
</> }
</> }
{ (customKeys.length > 0) &&
<>
<hr className="m-0 my-1"/>
{ furniKeys.map((key, index) =>
{
return (
<div key={ index } className="mb-1">
<div className="small text-wrap">{ key }</div>
<input type="text" className="form-control form-control-sm" value={ furniValues[index] } onChange={ event => onFurniSettingChange(index, event.target.value) }/>
</div>);
}) }
<Column gap={ 1 }>
{ customKeys.map((key, index) =>
{
return (
<Flex key={ index } alignItems="center" gap={ 1 }>
<Text small wrap align="end" variant="white" className="col-4">{ key }</Text>
<input type="text" className="form-control form-control-sm" value={ customValues[index] } onChange={ event => onCustomVariableChange(index, event.target.value) }/>
</Flex>);
}) }
</Column>
</> }
</> }
{ (customKeys.length > 0) &&
<>
<hr className="m-0 my-1"/>
{ customKeys.map((key, index) =>
{
return (
<div key={ index } className="mb-1">
<div className="small text-wrap">{ key }</div>
<input type="text" className="form-control form-control-sm" value={ customValues[index] } onChange={ event => onCustomVariableChange(index, event.target.value) }/>
</div>);
}) }
</> }
</InfoStandBaseView>
<div className="button-container mt-2">
</Column>
</Column>
</Column>
<Flex gap={ 1 } justifyContent="end">
{ canMove &&
<button type="button" className="btn btn-sm btn-dark" onClick={ event => processButtonAction('move') }>
<Button variant="dark" onClick={ event => processButtonAction('move') }>
{ LocalizeText('infostand.button.move') }
</button> }
</Button> }
{ canRotate &&
<button type="button" className="btn btn-sm btn-dark ms-1" onClick={ event => processButtonAction('rotate') }>
<Button variant="dark" onClick={ event => processButtonAction('rotate') }>
{ LocalizeText('infostand.button.rotate') }
</button> }
</Button> }
{ canUse &&
<button type="button" className="btn btn-sm btn-dark ms-1" onClick={ event => processButtonAction('use') }>
<Button variant="dark" onClick={ event => processButtonAction('use') }>
{ LocalizeText('infostand.button.use') }
</button>}
</Button>}
{ (pickupMode !== PICKUP_MODE_NONE) &&
<button type="button" className="btn btn-sm btn-dark ms-1" onClick={ event => processButtonAction('pickup') }>
<Button variant="dark" onClick={ event => processButtonAction('pickup') }>
{ LocalizeText((pickupMode === PICKUP_MODE_EJECT) ? 'infostand.button.eject' : 'infostand.button.pickup') }
</button> }
</Button> }
{ ((furniKeys.length > 0 && furniValues.length > 0) && (furniKeys.length === furniValues.length)) &&
<button className="btn btn-sm btn-dark ms-1" onClick={ () => processButtonAction('save_branding_configuration') }>
<Button variant="dark" onClick={ () => processButtonAction('save_branding_configuration') }>
{ LocalizeText('save') }
</button> }
</Button> }
{ ((customKeys.length > 0 && customValues.length > 0) && (customKeys.length === customValues.length)) &&
<button className="btn btn-sm btn-dark ms-1" onClick={ () => processButtonAction('save_custom_variables') }>
Set values
</button> }
</div>
</>
<Button variant="dark" onClick={ () => processButtonAction('save_custom_variables') }>
{ LocalizeText('save') }
</Button> }
</Flex>
</Column>
);
}

View File

@ -0,0 +1,85 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react';
import { LocalizeText, RoomWidgetUpdateInfostandPetEvent } from '../../../../api';
import { Base, Column, Flex, Text } from '../../../../common';
import { UserProfileIconView } from '../../../../layout';
import { PetImageView } from '../../../shared/pet-image/PetImageView';
interface InfoStandWidgetPetViewProps
{
petData: RoomWidgetUpdateInfostandPetEvent;
close: () => void;
}
export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
{
const { petData = null, close = null } = props;
if(!petData) return null;
return (
<Column className="nitro-infostand rounded">
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
<Column gap={ 1 }>
<Flex alignItems="center" justifyContent="between" gap={ 1 }>
<Text variant="white" small wrap>{ petData.name }</Text>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<Text variant="white" small wrap>{ LocalizeText(`pet.breed.${ petData.petType }.${ petData.petBreed }`) }</Text>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex gap={ 1 }>
<Column fullWidth overflow="hidden" className="body-image pet p-1">
<PetImageView figure={ petData.petFigure } posture={ petData.posture } direction={ 4 } />
</Column>
<Column grow gap={ 1 }>
<Text variant="white" center small wrap>{ LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ petData.level.toString(), petData.maximumLevel.toString() ]) }</Text>
<Column alignItems="center" gap={ 1 }>
<Text variant="white" small truncate>{ LocalizeText('infostand.pet.text.happiness') }</Text>
<Base fullWidth overflow="hidden" position="relative" className="bg-light-dark rounded">
<Flex fit center position="absolute">
<Text variant="white" small>{ petData.happyness + '/' + petData.maximumHappyness }</Text>
</Flex>
<Base className="bg-info rounded pet-stats" style={{ width: (petData.happyness / petData.maximumHappyness) * 100 + '%' }} />
</Base>
</Column>
<Column alignItems="center" gap={ 1 }>
<Text variant="white" small truncate>{ LocalizeText('infostand.pet.text.experience') }</Text>
<Base fullWidth overflow="hidden" position="relative" className="bg-light-dark rounded">
<Flex fit center position="absolute">
<Text variant="white" small>{ petData.experience + '/' + petData.levelExperienceGoal }</Text>
</Flex>
<Base className="bg-purple rounded pet-stats" style={{ width: (petData.experience / petData.levelExperienceGoal) * 100 + '%' }} />
</Base>
</Column>
<Column alignItems="center" gap={ 1 }>
<Text variant="white" small truncate>{ LocalizeText('infostand.pet.text.energy') }</Text>
<Base fullWidth overflow="hidden" position="relative" className="bg-light-dark rounded">
<Flex fit center position="absolute">
<Text variant="white" small>{ petData.energy + '/' + petData.maximumEnergy }</Text>
</Flex>
<Base className="bg-success rounded pet-stats" style={{ width: (petData.energy / petData.maximumEnergy) * 100 + '%' }} />
</Base>
</Column>
</Column>
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Text variant="white" small wrap>{ LocalizeText('infostand.text.petrespect', [ 'count' ], [ petData.respect.toString() ]) }</Text>
<Text variant="white" small wrap>{ LocalizeText('pet.age', [ 'age' ], [ petData.age.toString() ]) }</Text>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex alignItems="center" gap={ 1 }>
<UserProfileIconView userId={ petData.ownerId } />
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.petowner', [ 'name' ], [ petData.ownerName ]) }
</Text>
</Flex>
</Column>
</Column>
</Column>
);
}

View File

@ -0,0 +1,89 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { BotRemoveComposer } from '@nitrots/nitro-renderer';
import { FC, useMemo } from 'react';
import { LocalizeText, RoomWidgetUpdateInfostandRentableBotEvent } from '../../../../api';
import { Button, Column, Flex, Text } from '../../../../common';
import { SendMessageHook } from '../../../../hooks';
import { UserProfileIconView } from '../../../../layout';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
import { BotSkillsEnum } from '../avatar-info/common/BotSkillsEnum';
interface InfoStandWidgetRentableBotViewProps
{
rentableBotData: RoomWidgetUpdateInfostandRentableBotEvent;
close: () => void;
}
export const InfoStandWidgetRentableBotView: FC<InfoStandWidgetRentableBotViewProps> = props =>
{
const { rentableBotData = null, close = null } = props;
const canPickup = useMemo(() =>
{
if(rentableBotData.botSkills.indexOf(BotSkillsEnum.NO_PICK_UP) >= 0) return false;
if(!rentableBotData.amIOwner && !rentableBotData.amIAnyRoomController) return false;
return true;
}, [ rentableBotData ]);
const pickupBot = () => SendMessageHook(new BotRemoveComposer(rentableBotData.webID));
if(!rentableBotData) return;
return (
<Column gap={ 1 }>
<Column className="nitro-infostand rounded">
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
<Column gap={ 1 }>
<Flex alignItems="center" justifyContent="between" gap={ 1 }>
<Text variant="white" small wrap>{ rentableBotData.name }</Text>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex gap={ 1 }>
<Column fullWidth className="body-image bot">
<AvatarImageView figure={ rentableBotData.figure } direction={ 4 } />
</Column>
<Column grow center gap={ 0 }>
{ (rentableBotData.badges.length > 0) && rentableBotData.badges.map(result =>
{
return <BadgeImageView key={ result } badgeCode={ result } showInfo={ true } />;
}) }
</Column>
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex alignItems="center" className="bg-light-dark rounded py-1 px-2">
<Text fullWidth wrap textBreak variant="white" small className="motto-content">{ rentableBotData.motto }</Text>
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex alignItems="center" gap={ 1 }>
<UserProfileIconView userId={ rentableBotData.ownerId } />
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.botowner', [ 'name' ], [ rentableBotData.ownerName ]) }
</Text>
</Flex>
{ (rentableBotData.carryItem > 0) &&
<>
<hr className="m-0" />
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + rentableBotData.carryItem) ]) }
</Text>
</> }
</Column>
</Column>
</Column>
{ canPickup &&
<Flex justifyContent="end">
<Button variant="dark" onClick={ pickupBot }>{ LocalizeText('infostand.button.pickup') }</Button>
</Flex> }
</Column>
);
}

View File

@ -0,0 +1,47 @@
import { RelationshipStatusEnum, RelationshipStatusInfoMessageParser } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { GetUserProfile, LocalizeText } from '../../../../api';
import { Flex, Text } from '../../../../common';
interface InfoStandWidgetUserRelationshipsViewProps
{
relationships: RelationshipStatusInfoMessageParser;
}
interface InfoStandWidgetUserRelationshipsRelationshipViewProps
{
type: number;
}
export const InfoStandWidgetUserRelationshipsView: FC<InfoStandWidgetUserRelationshipsViewProps> = props =>
{
const { relationships = null } = props;
const RelationshipComponent = ({ type }: InfoStandWidgetUserRelationshipsRelationshipViewProps) =>
{
const relationshipInfo = (relationships && relationships.relationshipStatusMap.hasKey(type)) ? relationships.relationshipStatusMap.getValue(type) : null;
if(!relationshipInfo || !relationshipInfo.friendCount) return null;
const relationshipName = RelationshipStatusEnum.RELATIONSHIP_NAMES[type].toLocaleLowerCase();
return (
<Flex alignItems="center" gap={ 1 }>
<i className={`nitro-friends-spritesheet icon-${relationshipName}`} />
<Flex alignItems="center" gap={ 0 }>
<Text variant="white" onClick={ event => GetUserProfile(relationshipInfo.randomFriendId) }>{ relationshipInfo.randomFriendName }</Text>
{ (relationshipInfo.friendCount > 1) &&
<Text variant="white">{ ' ' + LocalizeText(`extendedprofile.relstatus.others.${ relationshipName }`, [ 'count' ], [ (relationshipInfo.friendCount - 1).toString() ]) }</Text> }
</Flex>
</Flex>
);
}
return (
<>
<RelationshipComponent type={ RelationshipStatusEnum.HEART } />
<RelationshipComponent type={ RelationshipStatusEnum.SMILE } />
<RelationshipComponent type={ RelationshipStatusEnum.BOBBA } />
</>
);
}

View File

@ -0,0 +1,177 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomSessionUserBadgesEvent, UserRelationshipsComposer } from '@nitrots/nitro-renderer';
import { FC, FocusEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetGroupInformation, LocalizeText, RoomWidgetChangeMottoMessage, RoomWidgetUpdateInfostandUserEvent } from '../../../../api';
import { Column, Text } from '../../../../common';
import { Base } from '../../../../common/Base';
import { Flex } from '../../../../common/Flex';
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../hooks/events';
import { UserProfileIconView } from '../../../../layout';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
import { useRoomContext } from '../../context/RoomContext';
import { InfoStandWidgetUserRelationshipsView } from './InfoStandWidgetUserRelationshipsView';
interface InfoStandWidgetUserViewProps
{
userData: RoomWidgetUpdateInfostandUserEvent;
close: () => void;
}
export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =>
{
const { userData = null, close = null } = props;
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ badges, setBadges ] = useState<string[]>([]);
const [ motto, setMotto ] = useState(null);
const [ isEditingMotto, setIsEditingMotto ] = useState(false);
const [ userRelationships, setUserRelationships ] = useState<RelationshipStatusInfoMessageParser>(null);
const maxBadgeCount = GetConfiguration<number>('user.badges.max.slots', 5);
const saveMotto = (motto: string) =>
{
if(motto.length > 38) return;
widgetHandler.processWidgetMessage(new RoomWidgetChangeMottoMessage(motto));
setIsEditingMotto(false);
}
const onMottoBlur = (event: FocusEvent<HTMLInputElement>) => saveMotto(event.target.value);
const onMottoKeyDown = (event: KeyboardEvent<HTMLInputElement>) =>
{
event.stopPropagation();
switch(event.key)
{
case 'Enter':
saveMotto((event.target as HTMLInputElement).value);
return;
}
}
const onRoomSessionUserBadgesEvent = useCallback((event: RoomSessionUserBadgesEvent) =>
{
if(!userData || (userData.webID !== event.userId)) return;
setBadges(event.badges);
}, [ userData ]);
CreateEventDispatcherHook(RoomSessionUserBadgesEvent.RSUBE_BADGES, eventDispatcher, onRoomSessionUserBadgesEvent);
const onUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) =>
{
const parser = event.getParser();
if(!userData || (userData.webID !== parser.userId)) return;
setUserRelationships(parser);
}, [ userData ]);
CreateMessageHook(RelationshipStatusInfoEvent, onUserRelationshipsEvent);
useEffect(() =>
{
BatchUpdates(() =>
{
setBadges(userData.badges);
setIsEditingMotto(false);
setMotto(userData.motto);
});
SendMessageHook(new UserRelationshipsComposer(userData.webID));
return () =>
{
setBadges([]);
setUserRelationships(null);
}
}, [ userData ]);
if(!userData) return null;
return (
<Column className="nitro-infostand rounded">
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
<Column gap={ 1 }>
<Flex alignItems="center" justifyContent="between">
<Flex alignItems="center" gap={ 1 }>
<UserProfileIconView userId={ userData.webID } />
<Text variant="white" small wrap>{ userData.name }</Text>
</Flex>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex gap={ 1 }>
<Column fullWidth className="body-image">
<AvatarImageView figure={ userData.figure } direction={ 4 } />
</Column>
<Column grow gap={ 0 }>
<Flex gap={ 1 }>
<Base className="badge-image">
{ badges[0] && <BadgeImageView badgeCode={ badges[0] } showInfo={ true } /> }
</Base>
<Base pointer={ ( userData.groupId > 0) } className="badge-image" onClick={ event => GetGroupInformation(userData.groupId) }>
{ userData.groupId > 0 &&
<BadgeImageView badgeCode={ userData.groupBadgeId } isGroup={ true } showInfo={ true } customTitle={ userData.groupName } /> }
</Base>
</Flex>
<Flex gap={ 1 }>
<Base className="badge-image">
{ badges[1] && <BadgeImageView badgeCode={ badges[1] } showInfo={ true } /> }
</Base>
<Base className="badge-image">
{ badges[2] && <BadgeImageView badgeCode={ badges[2] } showInfo={ true } /> }
</Base>
</Flex>
<Flex gap={ 1 }>
<Base className="badge-image">
{ badges[3] && <BadgeImageView badgeCode={ badges[3] } showInfo={ true } /> }
</Base>
<Base className="badge-image">
{ badges[4] && <BadgeImageView badgeCode={ badges[4] } showInfo={ true } /> }
</Base>
</Flex>
</Column>
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Flex alignItems="center" className="bg-light-dark rounded py-1 px-2">
{ (userData.type !== RoomWidgetUpdateInfostandUserEvent.OWN_USER) &&
<Text fullWidth wrap textBreak variant="white" small className="motto-content">{ motto }</Text> }
{ userData.type === RoomWidgetUpdateInfostandUserEvent.OWN_USER &&
<Flex grow alignItems="center" gap={ 2 }>
<FontAwesomeIcon icon="pencil-alt" className="small" />
{ !isEditingMotto &&
<Text fullWidth pointer wrap textBreak small variant="white" className="motto-content" onClick={ event => setIsEditingMotto(true) }>{ motto }</Text> }
{ isEditingMotto &&
<input type="text" className="motto-input" maxLength={ 38 } value={ motto } onChange={ event => setMotto(event.target.value) } onBlur={ onMottoBlur } onKeyDown={ onMottoKeyDown } autoFocus={ true } /> }
</Flex> }
</Flex>
<hr className="m-0" />
</Column>
<Column gap={ 1 }>
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.achievement_score') + ' ' + userData.achievementScore }
</Text>
{ (userData.carryItem > 0) &&
<>
<hr className="m-0" />
<Text variant="white" small wrap>
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + userData.carryItem) ]) }
</Text>
</> }
</Column>
<Column gap={ 1 }>
<InfoStandWidgetUserRelationshipsView relationships={ userRelationships } />
</Column>
</Column>
</Column>
);
}

View File

@ -4,6 +4,7 @@
bottom: $toolbar-height + 10px;
pointer-events: none;
z-index: $infostand-zindex;
color: $white;
.nitro-infostand {
position: relative;
@ -28,7 +29,6 @@
width: 100%;
max-width: 68px;
border-radius: $border-radius;
margin-right: 5px;
&.pet {
max-width: 75px;

View File

@ -1,12 +1,13 @@
import { FC, useCallback, useState } from 'react';
import { RoomWidgetRoomObjectMessage, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { Column } from '../../../../common';
import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base';
import { useRoomContext } from '../../context/RoomContext';
import { InfoStandWidgetBotView } from './views/bot/InfoStandWidgetBotView';
import { InfoStandWidgetFurniView } from './views/furni/InfoStandWidgetFurniView';
import { InfoStandWidgetPetView } from './views/pet/InfoStandWidgetPetView';
import { InfoStandWidgetRentableBotView } from './views/rentable-bot/InfoStandWidgetRentableBotView';
import { InfoStandWidgetUserView } from './views/user/InfoStandWidgetUserView';
import { InfoStandWidgetBotView } from './InfoStandWidgetBotView';
import { InfoStandWidgetFurniView } from './InfoStandWidgetFurniView';
import { InfoStandWidgetPetView } from './InfoStandWidgetPetView';
import { InfoStandWidgetRentableBotView } from './InfoStandWidgetRentableBotView';
import { InfoStandWidgetUserView } from './InfoStandWidgetUserView';
export const InfoStandWidgetView: FC<{}> = props =>
{
@ -116,8 +117,8 @@ export const InfoStandWidgetView: FC<{}> = props =>
if(!infoStandEvent) return null;
return (
<div className="d-flex flex-column align-items-end nitro-infostand-container">
<Column alignItems="end" className="nitro-infostand-container">
{ getInfostandView() }
</div>
</Column>
);
}

View File

@ -1,20 +0,0 @@
import { FC } from 'react';
import { InfoStandBaseViewProps } from './InfoStandBaseView.types';
export const InfoStandBaseView: FC<InfoStandBaseViewProps> = props =>
{
const { headerText = null, onCloseClick = null, children = null } = props;
return (
<div className="d-flex flex-column nitro-card nitro-infostand rounded">
<div className="container-fluid content-area overflow-visible">
<div className="d-flex justify-content-between align-items-center">
<div className="small text-wrap">{ headerText }</div>
<i className="fas fa-times cursor-pointer" onClick={ onCloseClick }></i>
</div>
<hr className="m-0 my-1" />
{ children }
</div>
</div>
);
}

View File

@ -1,7 +0,0 @@
import { MouseEvent, ReactNode } from 'react';
export interface InfoStandBaseViewProps
{
headerText: ReactNode;
onCloseClick: (event: MouseEvent) => void;
}

View File

@ -1,44 +0,0 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { InfoStandBaseView } from '../base/InfoStandBaseView';
import { InfoStandWidgetBotViewProps } from './InfoStandWidgetBotView.types';
export const InfoStandWidgetBotView: FC<InfoStandWidgetBotViewProps> = props =>
{
const { botData = null, close = null } = props;
const processButtonAction = useCallback((action: string) =>
{
if(!action || (action === '')) return;
}, []);
if(!botData) return null;
return (
<InfoStandBaseView headerText={ botData.name } onCloseClick={ close }>
<div className="d-flex">
<div className="body-image bot w-100">
<AvatarImageView figure={ botData.figure } direction={ 4 } />
</div>
<div className="w-100 d-flex justify-content-center align-items-center">
{ (botData.badges.length > 0) && botData.badges.map(result =>
{
return <BadgeImageView key={ result } badgeCode={ result } showInfo={ true } />;
}) }
</div>
</div>
<hr className="m-0 my-1" />
<div className="motto-content small">{ botData.motto }</div>
{ (botData.carryItem > 0) &&
<>
<hr className="m-0 my-1" />
<div className="small text-wrap">
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + botData.carryItem) ]) }
</div>
</> }
</InfoStandBaseView>
);
}

View File

@ -1,7 +0,0 @@
import { RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api';
export interface InfoStandWidgetBotViewProps
{
botData: RoomWidgetUpdateInfostandUserEvent;
close: () => void;
}

View File

@ -1,7 +0,0 @@
import { RoomWidgetUpdateInfostandFurniEvent } from '../../../../../../api';
export interface InfoStandWidgetFurniViewProps
{
furniData: RoomWidgetUpdateInfostandFurniEvent;
close: () => void;
}

View File

@ -1,55 +0,0 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../../../api';
import { UserProfileIconView } from '../../../../../../layout';
import { PetImageView } from '../../../../../shared/pet-image/PetImageView';
import { InfoStandBaseView } from '../base/InfoStandBaseView';
import { InfoStandWidgetPetViewProps } from './InfoStandWidgetPetView.types';
export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
{
const { petData = null, close = null } = props;
if(!petData) return null;
return (
<InfoStandBaseView headerText={ <>{ petData.name }<br />{ LocalizeText('pet.breed.' + petData.petType + '.' + petData.petBreed) }</> } onCloseClick={ close }>
<div className="d-flex">
<div className="body-image pet w-100">
<PetImageView figure={ petData.petFigure } posture={ petData.posture } direction={ 4 } />
</div>
<div className="w-100 d-flex flex-column align-items-center">
<div className="small text-center mb-1">{ LocalizeText('pet.level', ['level', 'maxlevel'], [petData.level.toString(), petData.maximumLevel.toString()]) }</div>
<div className="text-center mb-1 w-100">
<div className="small text-wrap mb-1">{ LocalizeText('infostand.pet.text.happiness') }</div>
<div className="bg-light-dark rounded position-relative overflow-hidden">
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{ petData.happyness + '/' + petData.maximumHappyness }</div>
<div className="bg-info rounded pet-stats" style={{ width: (petData.happyness / petData.maximumHappyness) * 100 + '%' }} />
</div>
</div>
<div className="text-center mb-1 w-100">
<div className="small text-wrap mb-1">{ LocalizeText('infostand.pet.text.experience') }</div>
<div className="bg-light-dark rounded position-relative overflow-hidden">
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{ petData.experience + '/' + petData.levelExperienceGoal }</div>
<div className="small bg-purple rounded pet-stats" style={{ width: ((petData.experience / petData.levelExperienceGoal) * 100 + '%') }} />
</div>
</div>
<div className="text-center w-100">
<div className="small text-wrap mb-1">{ LocalizeText('infostand.pet.text.energy') }</div>
<div className="bg-light-dark rounded position-relative overflow-hidden">
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{ petData.energy + '/' + petData.maximumEnergy }</div>
<div className="small bg-success rounded pet-stats" style={{ width: (petData.energy/petData.maximumEnergy)*100 +'%' }}></div>
</div>
</div>
</div>
</div>
<hr className="m-0 my-1" />
<div className="small text-wrap">{ LocalizeText('infostand.text.petrespect', ['count'], [petData.respect.toString()]) }</div>
<div className="small text-wrap">{ LocalizeText('pet.age', ['age'], [petData.age.toString()]) }</div>
<hr className="m-0 my-1" />
<div className="d-flex align-items-center">
<UserProfileIconView userId={ petData.ownerId } />
<div className="small text-wrap">{ LocalizeText('infostand.text.petowner', ['name'], [petData.ownerName]) }</div>
</div>
</InfoStandBaseView>
);
}

View File

@ -1,7 +0,0 @@
import { RoomWidgetUpdateInfostandPetEvent } from '../../../../../../api';
export interface InfoStandWidgetPetViewProps
{
petData: RoomWidgetUpdateInfostandPetEvent;
close: () => void;
}

View File

@ -1,70 +0,0 @@
import { BotRemoveComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo } from 'react';
import { LocalizeText } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks';
import { UserProfileIconView } from '../../../../../../layout';
import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { BotSkillsEnum } from '../../../avatar-info/common/BotSkillsEnum';
import { InfoStandBaseView } from '../base/InfoStandBaseView';
import { InfoStandWidgetRentableBotViewProps } from './InfoStandWidgetRentableBotView.types';
export const InfoStandWidgetRentableBotView: FC<InfoStandWidgetRentableBotViewProps> = props =>
{
const { rentableBotData = null, close = null } = props;
const canPickup = useMemo(() =>
{
if(rentableBotData.botSkills.indexOf(BotSkillsEnum.NO_PICK_UP) >= 0) return false;
if(!rentableBotData.amIOwner && !rentableBotData.amIAnyRoomController) return false;
return true;
}, [ rentableBotData ]);
const pickupBot = useCallback(() =>
{
SendMessageHook(new BotRemoveComposer(rentableBotData.webID));
}, [ rentableBotData ]);
if(!rentableBotData) return;
return (
<>
<InfoStandBaseView headerText={ rentableBotData.name } onCloseClick={ close }>
<div className="d-flex">
<div className="body-image bot w-100">
<AvatarImageView figure={ rentableBotData.figure } direction={ 4 } />
</div>
<div className="w-100 d-flex justify-content-center align-items-center">
{ (rentableBotData.badges.length > 0) && rentableBotData.badges.map(result =>
{
return <BadgeImageView key={ result } badgeCode={ result } showInfo={ true } />;
}) }
</div>
</div>
<hr className="m-0 my-1" />
<div className="motto-content small">{ rentableBotData.motto }</div>
<hr className="m-0 my-1" />
<div className="d-flex align-items-center">
<UserProfileIconView userId={ rentableBotData.ownerId } />
<div className="small text-wrap">{ LocalizeText('infostand.text.botowner', [ 'name' ], [ rentableBotData.ownerName ]) }</div>
</div>
{ (rentableBotData.carryItem > 0) &&
<>
<hr className="m-0 my-1"/>
<div className="small text-wrap">
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + rentableBotData.carryItem) ]) }
</div>
</> }
</InfoStandBaseView>
{ canPickup &&
<div className="button-container mt-2">
<button type="button" className="btn btn-sm btn-danger ms-1" onClick={ pickupBot }>
<i className="me-1 fas fa-box-open"></i>
{ LocalizeText('infostand.button.pickup') }
</button>
</div> }
</>
);
}

View File

@ -1,7 +0,0 @@
import { RoomWidgetUpdateInfostandRentableBotEvent } from '../../../../../../api';
export interface InfoStandWidgetRentableBotViewProps
{
rentableBotData: RoomWidgetUpdateInfostandRentableBotEvent;
close: () => void;
}

View File

@ -1,159 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomSessionUserBadgesEvent, UserRelationshipsComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames';
import { FC, FocusEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { GetGroupInformation, LocalizeText, RoomWidgetChangeMottoMessage, RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api';
import { Base } from '../../../../../../common/Base';
import { Flex } from '../../../../../../common/Flex';
import { RelationshipsContainerView } from '../../../../../../components/user-profile/views/RelationshipsContainerView';
import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../../hooks/events';
import { UserProfileIconView } from '../../../../../../layout';
import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { useRoomContext } from '../../../../context/RoomContext';
import { InfoStandWidgetUserViewProps } from './InfoStandWidgetUserView.types';
export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =>
{
const { userData = null, close = null } = props;
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ badges, setBadges ] = useState<string[]>([]);
const [ motto, setMotto ] = useState(null);
const [ isEditingMotto, setIsEditingMotto ] = useState(false);
const [ userRelationships, setUserRelationships ] = useState<RelationshipStatusInfoMessageParser>(null);
const saveMotto = useCallback((motto: string) =>
{
if(motto.length > 38) return;
widgetHandler.processWidgetMessage(new RoomWidgetChangeMottoMessage(motto));
setIsEditingMotto(false);
}, [ widgetHandler ]);
const onMottoBlur = useCallback((event: FocusEvent<HTMLInputElement>) =>
{
saveMotto(event.target.value);
}, [ saveMotto ]);
const onMottoKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
{
event.stopPropagation();
switch(event.key)
{
case 'Enter':
saveMotto((event.target as HTMLInputElement).value);
return;
}
}, [ saveMotto ]);
const onRoomSessionUserBadgesEvent = useCallback((event: RoomSessionUserBadgesEvent) =>
{
if(!userData || (userData.webID !== event.userId)) return;
setBadges(event.badges);
}, [ userData ]);
CreateEventDispatcherHook(RoomSessionUserBadgesEvent.RSUBE_BADGES, eventDispatcher, onRoomSessionUserBadgesEvent);
const onUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) =>
{
const parser = event.getParser();
if(userData && userData.webID === parser.userId)
setUserRelationships(parser);
}, [userData]);
CreateMessageHook(RelationshipStatusInfoEvent, onUserRelationshipsEvent);
useEffect(() =>
{
setBadges(userData.badges);
setIsEditingMotto(false);
setMotto(userData.motto);
SendMessageHook(new UserRelationshipsComposer(userData.webID));
return () =>
{
setBadges([]);
setUserRelationships(null);
}
}, [ userData ]);
if(!userData) return null;
return (
<Flex column className="nitro-card nitro-infostand rounded">
<div className="container-fluid content-area overflow-visible">
<Flex center>
<Flex alignItems="center">
<UserProfileIconView userId={ userData.webID } />
<span className="small text-wrap">{ userData.name }</span>
</Flex>
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
</Flex>
<hr className="m-0 my-1" />
<Flex>
<div className="body-image w-100">
<AvatarImageView figure={ userData.figure } direction={ 4 } />
</div>
<div>
<Flex justifyContent="between">
<div className="badge-image">
{ badges[0] && <BadgeImageView badgeCode={ badges[0] } showInfo={ true } /> }
</div>
<div className={ 'badge-image' + classNames({ ' cursor-pointer': userData.groupId > 0 }) } onClick={ () => GetGroupInformation(userData.groupId) }>
{ userData.groupId > 0 && <BadgeImageView badgeCode={ userData.groupBadgeId } isGroup={ true } showInfo={ true } customTitle={ userData.groupName } /> }
</div>
</Flex>
<Flex justifyContent="between">
<div className="badge-image">
{ badges[1] && <BadgeImageView badgeCode={ badges[1] } showInfo={ true } /> }
</div>
<div className="badge-image">
{ badges[2] && <BadgeImageView badgeCode={ badges[2] } showInfo={ true } /> }
</div>
</Flex>
<Flex justifyContent="between">
<div className="badge-image">
{ badges[3] && <BadgeImageView badgeCode={ badges[3] } showInfo={ true } /> }
</div>
<div className="badge-image">
{ badges[4] && <BadgeImageView badgeCode={ badges[4] } showInfo={ true } /> }
</div>
</Flex>
</div>
</Flex>
<hr className="m-0 my-1" />
<div className="bg-light-dark rounded py-1 px-2 small">
{ (userData.type !== RoomWidgetUpdateInfostandUserEvent.OWN_USER) &&
<div className="motto-content w-100 text-wrap text-break">{ motto }</div> }
{ userData.type === RoomWidgetUpdateInfostandUserEvent.OWN_USER &&
<Flex justifyContent="between" alignItems="center">
<FontAwesomeIcon icon="pencil-alt" className="small me-2" />
<Base fit>
{ !isEditingMotto &&
<div className="motto-content cursor-pointer w-100 text-wrap text-break" onClick={ event => setIsEditingMotto(true) }>{ motto }</div> }
{ isEditingMotto &&
<input type="text" className="motto-input" maxLength={ 38 } value={ motto } onChange={ event => setMotto(event.target.value) } onBlur={ onMottoBlur } onKeyDown={ onMottoKeyDown } autoFocus={ true } /> }
</Base>
</Flex> }
</div>
<hr className="m-0 my-1" />
<div className="small text-wrap">
{ LocalizeText('infostand.text.achievement_score') + ' ' + userData.achievementScore }
</div>
{ (userData.carryItem > 0) &&
<>
<hr className="m-0 my-1" />
<div className="small text-wrap">
{ LocalizeText('infostand.text.handitem', [ 'item' ], [ LocalizeText('handitem' + userData.carryItem) ]) }
</div>
</>
}
<RelationshipsContainerView relationships={userRelationships} simple={true}/>
</div>
</Flex>);
}

View File

@ -1,7 +0,0 @@
import { RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api';
export interface InfoStandWidgetUserViewProps
{
userData: RoomWidgetUpdateInfostandUserEvent;
close: () => void;
}