InventoryPetView.tsx 10 Ko
Télécharger le fichier
import { InventoryFilterSelect } from '../InventoryFilterSelect';
import { DeletePetMessageComposer, IRoomSession, PetType, RoomPreviewer } from '@octane/renderer';
import { FC, useEffect, useMemo, useState } from 'react';
import { FaTrashAlt } from 'react-icons/fa';
import { attemptPetPlacement, LocalizeText, localizeWithFallback, SendMessageComposer, UnseenItemCategory } from '../../../../api';
import { LayoutRarityLevelView } from '../../../../common';
import { ClassicScrollAreaView } from '../../../../common/scroll-area/ClassicScrollAreaView';
import { useInventoryPets, useInventoryUnseenTracker, useNotification, usePlayTestRestricted } from '../../../../hooks';
import { OctaneButton } from '../../../../layout';
import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryPetImageView } from './InventoryPetImageView';
import { InventoryPetItemView } from './InventoryPetItemView';
import { BaseButton } from '../../../../common/BaseButton';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
export const InventoryPetView: FC<{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}> = (props) => {
const { roomSession = null } = props;
const [isVisible, setIsVisible] = useState(false);
const { petItems = null, selectedPet = null, setSelectedPet, activate = null, deactivate = null } = useInventoryPets();
const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker();
const { showConfirm = null } = useNotification();
const playTestRestricted = usePlayTestRestricted();
const [searchInput, setSearchInput] = useState('');
const [search, setSearch] = useState('');
const [typeFilter, setTypeFilter] = useState(-1);
const availableTypes = useMemo(() => [...new Set((petItems ?? []).map(({ petData }) => petData.typeId))].sort((a, b) => a - b), [petItems]);
const activeType = availableTypes.includes(typeFilter) ? typeFilter : -1;
// Like the official client, only monster plants can be filtered by rarity.
const [rarityFilter, setRarityFilter] = useState(-1);
const rarityFilterEnabled = activeType === PetType.MONSTERPLANT;
const availableRarities = useMemo(
() =>
[
...new Set(
(petItems ?? []).filter(({ petData }) => petData.typeId === PetType.MONSTERPLANT && petData.rarityLevel >= 0).map(({ petData }) => petData.rarityLevel)
)
].sort((a, b) => a - b),
[petItems]
);
const activeRarity = rarityFilterEnabled && availableRarities.includes(rarityFilter) ? rarityFilter : -1;
const visiblePets = useMemo(
() =>
(petItems ?? []).filter(
({ petData }) =>
(activeType === -1 || petData.typeId === activeType) &&
(activeRarity === -1 || petData.rarityLevel === activeRarity) &&
(!search || petData.name.toLowerCase().includes(search) || LocalizeText(`pet.type.${petData.typeId}`).toLowerCase().includes(search))
),
[petItems, activeType, activeRarity, search]
);
useEffect(() => {
if (!visiblePets.includes(selectedPet)) setSelectedPet(visiblePets[0] ?? null);
}, [visiblePets, selectedPet, setSelectedPet]);
const attemptDeletePet = () => {
if (!selectedPet?.petData) return;
showConfirm(
localizeWithFallback(
'inventory.delete.confirm_delete.info',
`Delete ${selectedPet.petData.name}?`,
['furniname', 'amount'],
[selectedPet.petData.name, '1']
),
() => SendMessageComposer(new DeletePetMessageComposer(selectedPet.petData.id)),
null,
null,
null,
localizeWithFallback('inventory.delete.confirm_delete.title', 'Delete pet')
);
};
useEffect(() => {
if (!selectedPet || !isUnseen(UnseenItemCategory.PET, selectedPet.petData.id)) return;
removeUnseen(UnseenItemCategory.PET, selectedPet.petData.id);
}, [selectedPet, isUnseen, removeUnseen]);
useEffect(() => {
if (!isVisible) return;
const id = activate();
return () => deactivate(id);
}, [isVisible, activate, deactivate]);
useEffect(() => {
setIsVisible(true);
return () => setIsVisible(false);
}, []);
if (!petItems || !petItems.length) {
return <InventoryCategoryEmptyView desc={LocalizeText('inventory.empty.pets.desc')} title={LocalizeText('inventory.empty.pets.title')} />;
}
return (
<NativeTextTree>
<div className="octane-inventory-animals is-pets">
<div className="octane-inventory-filter-bar">
<div className="octane-inventory-filter-search">
<input
className="octane-inventory-filter-input"
aria-label={LocalizeText('generic.search')}
value={searchInput}
onChange={(event) => setSearchInput(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') setSearch(searchInput.toLowerCase());
if (event.key === 'Escape') {
setSearchInput('');
setSearch('');
}
}}
/>
{searchInput && (
<BaseButton
type="button"
className="octane-inventory-filter-clear"
aria-label={LocalizeText('generic.clear')}
onClick={() => {
setSearchInput('');
setSearch('');
}}
/>
)}
</div>
<InventoryFilterSelect
aria-label="Pet type"
value={activeType}
onChange={(value) => {
setTypeFilter(Number(value));
setSearch(searchInput.toLowerCase());
}}
>
<option value={-1}>
{LocalizeText('inventory.pets.filter.type.all') === 'inventory.pets.filter.type.all'
? 'All types'
: LocalizeText('inventory.pets.filter.type.all')}
</option>
{availableTypes.map((type) => (
<option key={type} value={type}>
{LocalizeText(`pet.type.${type}`)}
</option>
))}
</InventoryFilterSelect>
<InventoryFilterSelect
aria-label="Pet rarity"
disabled={!rarityFilterEnabled}
value={activeRarity}
onChange={(value) => setRarityFilter(Number(value))}
>
<option value={-1}>
{LocalizeText('inventory.pets.filter.rarity.all') === 'inventory.pets.filter.rarity.all'
? 'All rarities'
: LocalizeText('inventory.pets.filter.rarity.all')}
</option>
{rarityFilterEnabled &&
availableRarities.map((rarity) => (
<option key={rarity} value={rarity}>
{rarity}
</option>
))}
</InventoryFilterSelect>
</div>
<div className="octane-inventory-animal-grid">
<ClassicScrollAreaView className="size-full">
<div className="octane-inventory-animal-cells">
{visiblePets.map((item) => (
<InventoryPetItemView key={item.petData.id} petItem={item} />
))}
</div>
</ClassicScrollAreaView>
</div>
<div className="octane-inventory-animal-preview">
<div className="octane-inventory-animal-name">{selectedPet?.petData.name}</div>
<div className="octane-inventory-animal-image">{selectedPet && <InventoryPetImageView pet={selectedPet.petData} preview />}</div>
{selectedPet?.petData.rarityLevel >= 0 && (
<LayoutRarityLevelView className="octane-inventory-animal-preview-rarity" level={selectedPet.petData.rarityLevel} />
)}
<div className="octane-inventory-animal-description">{selectedPet && LocalizeText(`pet.type.${selectedPet.petData.typeId}`)}</div>
<div className="octane-inventory-animal-actions">
<OctaneButton
className="octane-inventory-animal-place"
disabled={!selectedPet || !(roomSession?.isRoomOwner || roomSession?.allowPets) || playTestRestricted}
onClick={() => attemptPetPlacement(selectedPet)}
>
{LocalizeText('inventory.pets.placetoroom')}
</OctaneButton>
<OctaneButton className="octane-inventory-btn-delete" disabled={!selectedPet} onClick={attemptDeletePet}>
<FaTrashAlt className="fa-icon" />
<span>{LocalizeText('generic.delete')}</span>
</OctaneButton>
</div>
</div>
</div>
</NativeTextTree>
);
};
