mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Update infostand
This commit is contained in:
parent
c7d3022ccb
commit
86d1d8ceba
57
src/views/room/widgets/infostand/InfoStandWidgetBotView.tsx
Normal file
57
src/views/room/widgets/infostand/InfoStandWidgetBotView.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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 { CrackableDataType, GroupInformationComposer, GroupInformationEvent, RoomControllerLevel, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnumItemExtradataParameter, RoomWidgetFurniInfoUsagePolicyEnum, SetObjectDataMessageComposer, StringDataType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../../api';
|
import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage, RoomWidgetUpdateInfostandFurniEvent } from '../../../../api';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
|
import { Button, Column, Flex, Text } from '../../../../common';
|
||||||
import { UserProfileIconView } from '../../../../../../layout';
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
|
import { UserProfileIconView } from '../../../../layout';
|
||||||
import { LimitedEditionCompactPlateView } from '../../../../../shared/limited-edition/LimitedEditionCompactPlateView';
|
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
|
||||||
import { RarityLevelView } from '../../../../../shared/rarity-level/RarityLevelView';
|
import { LimitedEditionCompactPlateView } from '../../../shared/limited-edition/LimitedEditionCompactPlateView';
|
||||||
import { useRoomContext } from '../../../../context/RoomContext';
|
import { RarityLevelView } from '../../../shared/rarity-level/RarityLevelView';
|
||||||
import { InfoStandBaseView } from '../base/InfoStandBaseView';
|
import { useRoomContext } from '../../context/RoomContext';
|
||||||
import { InfoStandWidgetFurniViewProps } from './InfoStandWidgetFurniView.types';
|
|
||||||
|
interface InfoStandWidgetFurniViewProps
|
||||||
|
{
|
||||||
|
furniData: RoomWidgetUpdateInfostandFurniEvent;
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const PICKUP_MODE_NONE: number = 0;
|
const PICKUP_MODE_NONE: number = 0;
|
||||||
const PICKUP_MODE_EJECT: number = 1;
|
const PICKUP_MODE_EJECT: number = 1;
|
||||||
@ -117,19 +123,22 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
|
|||||||
|
|
||||||
if(furniData.isStickie) pickupMode = PICKUP_MODE_NONE;
|
if(furniData.isStickie) pickupMode = PICKUP_MODE_NONE;
|
||||||
|
|
||||||
setPickupMode(pickupMode);
|
BatchUpdates(() =>
|
||||||
setCanMove(canMove);
|
{
|
||||||
setCanRotate(canRotate);
|
setPickupMode(pickupMode);
|
||||||
setCanUse(canUse);
|
setCanMove(canMove);
|
||||||
setFurniKeys(furniKeyss);
|
setCanRotate(canRotate);
|
||||||
setFurniValues(furniValuess);
|
setCanUse(canUse);
|
||||||
setCustomKeys(customKeyss);
|
setFurniKeys(furniKeyss);
|
||||||
setCustomValues(customValuess);
|
setFurniValues(furniValuess);
|
||||||
setIsCrackable(isCrackable);
|
setCustomKeys(customKeyss);
|
||||||
setCrackableHits(crackableHits);
|
setCustomValues(customValuess);
|
||||||
setCrackableTarget(crackableTarget);
|
setIsCrackable(isCrackable);
|
||||||
setGodMode(godMode);
|
setCrackableHits(crackableHits);
|
||||||
setGroupName(null);
|
setCrackableTarget(crackableTarget);
|
||||||
|
setGodMode(godMode);
|
||||||
|
setGroupName(null);
|
||||||
|
});
|
||||||
|
|
||||||
if(furniData.groupId) SendMessageHook(new GroupInformationComposer(furniData.groupId, false));
|
if(furniData.groupId) SendMessageHook(new GroupInformationComposer(furniData.groupId, false));
|
||||||
}, [ roomSession, furniData ]);
|
}, [ roomSession, furniData ]);
|
||||||
@ -247,97 +256,125 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
|
|||||||
if(!furniData) return null;
|
if(!furniData) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Column gap={ 1 } alignItems="end">
|
||||||
<InfoStandBaseView headerText={ furniData.name } onCloseClick={ close }>
|
<Column className="nitro-infostand rounded">
|
||||||
<div className="position-relative w-100">
|
<Column overflow="visible" className="container-fluid content-area" gap={ 1 }>
|
||||||
{ furniData.stuffData.isUnique &&
|
<Column gap={ 1 }>
|
||||||
<div className="position-absolute end-0">
|
<Flex alignItems="center" justifyContent="between" gap={ 1 }>
|
||||||
<LimitedEditionCompactPlateView uniqueNumber={ furniData.stuffData.uniqueNumber } uniqueSeries={ furniData.stuffData.uniqueSeries } />
|
<Text variant="white" small wrap>{ furniData.name }</Text>
|
||||||
</div> }
|
<FontAwesomeIcon icon="times" className="cursor-pointer" onClick={ close } />
|
||||||
{ (furniData.stuffData.rarityLevel > -1) &&
|
</Flex>
|
||||||
<div className="position-absolute end-0">
|
<hr className="m-0" />
|
||||||
<RarityLevelView level={ furniData.stuffData.rarityLevel } />
|
</Column>
|
||||||
</div> }
|
<Column gap={ 1 }>
|
||||||
{ furniData.image && furniData.image.src.length &&
|
<Flex position="relative" gap={ 1 }>
|
||||||
<img className="d-block mx-auto" src={ furniData.image.src } alt="" /> }
|
{ furniData.stuffData.isUnique &&
|
||||||
</div>
|
<div className="position-absolute end-0">
|
||||||
<hr className="m-0 my-1" />
|
<LimitedEditionCompactPlateView uniqueNumber={ furniData.stuffData.uniqueNumber } uniqueSeries={ furniData.stuffData.uniqueSeries } />
|
||||||
<div className="small text-wrap">{ furniData.description }</div>
|
</div> }
|
||||||
<hr className="m-0 my-1" />
|
{ (furniData.stuffData.rarityLevel > -1) &&
|
||||||
<div className="d-flex align-items-center">
|
<div className="position-absolute end-0">
|
||||||
<UserProfileIconView userId={ furniData.ownerId } />
|
<RarityLevelView level={ furniData.stuffData.rarityLevel } />
|
||||||
<div className="small text-wrap">{ LocalizeText('furni.owner', [ 'name' ], [ furniData.ownerName ]) }</div>
|
</div> }
|
||||||
</div>
|
{ furniData.image && furniData.image.src.length &&
|
||||||
{ (furniData.purchaseOfferId > 0) && <button type="button" className="btn btn-primary btn-sm mt-1" onClick={ event => processButtonAction('buy_one') }>{ LocalizeText('infostand.button.buy') }</button> }
|
<img className="d-block mx-auto" src={ furniData.image.src } alt="" /> }
|
||||||
{ isCrackable &&
|
</Flex>
|
||||||
<>
|
<hr className="m-0" />
|
||||||
<hr className="m-0 my-1" />
|
</Column>
|
||||||
<div className="small text-wrap">{ LocalizeText('infostand.crackable_furni.hits_remaining', [ 'hits', 'target' ], [ crackableHits.toString(), crackableTarget.toString() ]) }</div>
|
<Column gap={ 1 }>
|
||||||
</> }
|
<Text fullWidth wrap textBreak variant="white" small>{ furniData.description }</Text>
|
||||||
{ furniData.groupId > 0 &&
|
<hr className="m-0" />
|
||||||
<>
|
</Column>
|
||||||
<hr className="m-0 my-1" />
|
<Column gap={ 1 }>
|
||||||
<div className="d-flex align-items-center cursor-pointer text-decoration-underline gap-2" onClick={ () => GetGroupInformation(furniData.groupId) }>
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
<BadgeImageView badgeCode={ getGroupBadgeCode() } isGroup={ true } />
|
<UserProfileIconView userId={ furniData.ownerId } />
|
||||||
<div>{ groupName }</div>
|
<Text variant="white" small wrap>
|
||||||
</div>
|
{ LocalizeText('furni.owner', [ 'name' ], [ furniData.ownerName ]) }
|
||||||
</> }
|
</Text>
|
||||||
{ godMode &&
|
</Flex>
|
||||||
<>
|
{ (furniData.purchaseOfferId > 0) &&
|
||||||
<hr className="m-0 my-1" />
|
<Flex>
|
||||||
<div className="small text-wrap">ID: { furniData.id }</div>
|
<Text variant="white" small underline pointer onClick={ event => processButtonAction('buy_one') }>
|
||||||
{ (furniKeys.length > 0) &&
|
{ 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"/>
|
<hr className="m-0 my-1"/>
|
||||||
{ furniKeys.map((key, index) =>
|
<Column gap={ 1 }>
|
||||||
{
|
{ customKeys.map((key, index) =>
|
||||||
return (
|
{
|
||||||
<div key={ index } className="mb-1">
|
return (
|
||||||
<div className="small text-wrap">{ key }</div>
|
<Flex key={ index } alignItems="center" gap={ 1 }>
|
||||||
<input type="text" className="form-control form-control-sm" value={ furniValues[index] } onChange={ event => onFurniSettingChange(index, event.target.value) }/>
|
<Text small wrap align="end" variant="white" className="col-4">{ key }</Text>
|
||||||
</div>);
|
<input type="text" className="form-control form-control-sm" value={ customValues[index] } onChange={ event => onCustomVariableChange(index, event.target.value) }/>
|
||||||
}) }
|
</Flex>);
|
||||||
|
}) }
|
||||||
|
</Column>
|
||||||
</> }
|
</> }
|
||||||
</> }
|
</Column>
|
||||||
{ (customKeys.length > 0) &&
|
</Column>
|
||||||
<>
|
</Column>
|
||||||
<hr className="m-0 my-1"/>
|
<Flex gap={ 1 } justifyContent="end">
|
||||||
{ 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">
|
|
||||||
{ canMove &&
|
{ 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') }
|
{ LocalizeText('infostand.button.move') }
|
||||||
</button> }
|
</Button> }
|
||||||
{ canRotate &&
|
{ 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') }
|
{ LocalizeText('infostand.button.rotate') }
|
||||||
</button> }
|
</Button> }
|
||||||
{ canUse &&
|
{ 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') }
|
{ LocalizeText('infostand.button.use') }
|
||||||
</button>}
|
</Button>}
|
||||||
{ (pickupMode !== PICKUP_MODE_NONE) &&
|
{ (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') }
|
{ LocalizeText((pickupMode === PICKUP_MODE_EJECT) ? 'infostand.button.eject' : 'infostand.button.pickup') }
|
||||||
</button> }
|
</Button> }
|
||||||
{ ((furniKeys.length > 0 && furniValues.length > 0) && (furniKeys.length === furniValues.length)) &&
|
{ ((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') }
|
{ LocalizeText('save') }
|
||||||
</button> }
|
</Button> }
|
||||||
{ ((customKeys.length > 0 && customValues.length > 0) && (customKeys.length === customValues.length)) &&
|
{ ((customKeys.length > 0 && customValues.length > 0) && (customKeys.length === customValues.length)) &&
|
||||||
<button className="btn btn-sm btn-dark ms-1" onClick={ () => processButtonAction('save_custom_variables') }>
|
<Button variant="dark" onClick={ () => processButtonAction('save_custom_variables') }>
|
||||||
Set values
|
{ LocalizeText('save') }
|
||||||
</button> }
|
</Button> }
|
||||||
</div>
|
</Flex>
|
||||||
</>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
85
src/views/room/widgets/infostand/InfoStandWidgetPetView.tsx
Normal file
85
src/views/room/widgets/infostand/InfoStandWidgetPetView.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
@ -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 } />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
177
src/views/room/widgets/infostand/InfoStandWidgetUserView.tsx
Normal file
177
src/views/room/widgets/infostand/InfoStandWidgetUserView.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
bottom: $toolbar-height + 10px;
|
bottom: $toolbar-height + 10px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: $infostand-zindex;
|
z-index: $infostand-zindex;
|
||||||
|
color: $white;
|
||||||
|
|
||||||
.nitro-infostand {
|
.nitro-infostand {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -28,7 +29,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 68px;
|
max-width: 68px;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
margin-right: 5px;
|
|
||||||
|
|
||||||
&.pet {
|
&.pet {
|
||||||
max-width: 75px;
|
max-width: 75px;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { RoomWidgetRoomObjectMessage, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
|
import { RoomWidgetRoomObjectMessage, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
|
||||||
|
import { Column } from '../../../../common';
|
||||||
import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base';
|
import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base';
|
||||||
import { useRoomContext } from '../../context/RoomContext';
|
import { useRoomContext } from '../../context/RoomContext';
|
||||||
import { InfoStandWidgetBotView } from './views/bot/InfoStandWidgetBotView';
|
import { InfoStandWidgetBotView } from './InfoStandWidgetBotView';
|
||||||
import { InfoStandWidgetFurniView } from './views/furni/InfoStandWidgetFurniView';
|
import { InfoStandWidgetFurniView } from './InfoStandWidgetFurniView';
|
||||||
import { InfoStandWidgetPetView } from './views/pet/InfoStandWidgetPetView';
|
import { InfoStandWidgetPetView } from './InfoStandWidgetPetView';
|
||||||
import { InfoStandWidgetRentableBotView } from './views/rentable-bot/InfoStandWidgetRentableBotView';
|
import { InfoStandWidgetRentableBotView } from './InfoStandWidgetRentableBotView';
|
||||||
import { InfoStandWidgetUserView } from './views/user/InfoStandWidgetUserView';
|
import { InfoStandWidgetUserView } from './InfoStandWidgetUserView';
|
||||||
|
|
||||||
export const InfoStandWidgetView: FC<{}> = props =>
|
export const InfoStandWidgetView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
@ -116,8 +117,8 @@ export const InfoStandWidgetView: FC<{}> = props =>
|
|||||||
if(!infoStandEvent) return null;
|
if(!infoStandEvent) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-flex flex-column align-items-end nitro-infostand-container">
|
<Column alignItems="end" className="nitro-infostand-container">
|
||||||
{ getInfostandView() }
|
{ getInfostandView() }
|
||||||
</div>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { MouseEvent, ReactNode } from 'react';
|
|
||||||
|
|
||||||
export interface InfoStandBaseViewProps
|
|
||||||
{
|
|
||||||
headerText: ReactNode;
|
|
||||||
onCloseClick: (event: MouseEvent) => void;
|
|
||||||
}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api';
|
|
||||||
|
|
||||||
export interface InfoStandWidgetBotViewProps
|
|
||||||
{
|
|
||||||
botData: RoomWidgetUpdateInfostandUserEvent;
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { RoomWidgetUpdateInfostandFurniEvent } from '../../../../../../api';
|
|
||||||
|
|
||||||
export interface InfoStandWidgetFurniViewProps
|
|
||||||
{
|
|
||||||
furniData: RoomWidgetUpdateInfostandFurniEvent;
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { RoomWidgetUpdateInfostandPetEvent } from '../../../../../../api';
|
|
||||||
|
|
||||||
export interface InfoStandWidgetPetViewProps
|
|
||||||
{
|
|
||||||
petData: RoomWidgetUpdateInfostandPetEvent;
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
@ -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> }
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { RoomWidgetUpdateInfostandRentableBotEvent } from '../../../../../../api';
|
|
||||||
|
|
||||||
export interface InfoStandWidgetRentableBotViewProps
|
|
||||||
{
|
|
||||||
rentableBotData: RoomWidgetUpdateInfostandRentableBotEvent;
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
@ -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>);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api';
|
|
||||||
|
|
||||||
export interface InfoStandWidgetUserViewProps
|
|
||||||
{
|
|
||||||
userData: RoomWidgetUpdateInfostandUserEvent;
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user