pet-companion-front/src/components/InteractionMenu.tsx

210 lines
6.4 KiB
TypeScript
Executable File

import { Pizza, PlayCircle, Moon, Compass, Sword, FeatherIcon } from 'lucide-react';
import CollectResourcesButton from './CollectResourcesButton';
import { Pet } from '../types/Pet';
import { useState, useEffect } from 'react';
import { updatePetAction, getPetGatheredResources } from '../services/api/api';
import { PetActionGathered, PetBasicAction, PetGatherAction } from '../types/PetAction';
import ActionButton from './button/ActionButton';
import ActionResourceButton from './ActionResourceButton';
import ResourceSelectionModal from './modal/ResourceSelectionModal';
interface InteractionMenuProps {
pet: Pet;
onPetUpdate: (updatedPet: Pet) => void;
onCustomize: () => void;
}
export default function InteractionMenu({ pet, onPetUpdate }: InteractionMenuProps) {
const [gatheredResources, setGatheredResources] = useState<PetActionGathered[]>([]);
const [remainingCooldown, setRemainingCooldown] = useState<number | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
useEffect(() => {
const updateCooldown = () => {
if (!pet.basicActionCooldown) {
setRemainingCooldown(null);
return;
}
const cooldownTime = new Date(pet.basicActionCooldown).getTime();
const now = new Date().getTime();
const remaining = Math.max(0, cooldownTime - now);
if (remaining === 0) {
setRemainingCooldown(null);
} else {
setRemainingCooldown(remaining);
}
};
updateCooldown();
const interval = setInterval(updateCooldown, 1000);
return () => clearInterval(interval);
}, [pet.basicActionCooldown]);
useEffect(() => {
const fetchGatheredResources = async () => {
if (pet.petGatherAction === 'IDLE') {
return;
}
try {
const resources = await getPetGatheredResources(pet.id);
setGatheredResources(resources);
} catch (error) {
console.error('Failed to fetch gathered resources:', error);
}
};
fetchGatheredResources();
const interval = setInterval(fetchGatheredResources, 10000); // Poll every 10 seconds
return () => clearInterval(interval);
}, [pet.id, pet.petGatherAction]);
const formatCooldownTime = (ms: number) => {
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
if (minutes > 0) {
return `${minutes} minute${minutes > 1 ? 's' : ''} remaining`;
}
return `${seconds} second${seconds > 1 ? 's' : ''} remaining`;
};
const handleGatherComplete = (updatedPet: Pet) => {
onPetUpdate(updatedPet);
};
function performBasicAction(basicAction: PetBasicAction): () => void {
return async () => {
try {
const updatedPet = await updatePetAction(pet.id, { basicAction: basicAction });
onPetUpdate(updatedPet);
} catch (error) {
console.error('Failed to perform basic action:', error);
}
};
}
const handleActionStart = async (actionType: 'gather' | 'explore' | 'battle') => {
if (actionType === 'gather') {
setIsModalOpen(true);
return;
}
try {
const action: PetGatherAction = actionType === 'explore' ? 'EXPLORE' : 'BATTLE';
const updatedPet = await updatePetAction(pet.id, { gatherAction: action });
onPetUpdate(updatedPet);
} catch (error) {
console.error('Failed to start action:', error);
}
};
const handleResourceSelect = async (resourceType: string) => {
if (resourceType === 'stop') {
try {
const updatedPet = await updatePetAction(pet.id, { gatherAction: 'IDLE' });
onPetUpdate(updatedPet);
} catch (error) {
console.error('Failed to stop action:', error);
}
setIsModalOpen(false);
return;
}
try {
const action: PetGatherAction = `GATHERING_${resourceType.toUpperCase()}` as PetGatherAction;
const updatedPet = await updatePetAction(pet.id, { gatherAction: action });
onPetUpdate(updatedPet);
} catch (error) {
console.error('Failed to start gathering:', error);
} finally {
setIsModalOpen(false);
}
};
const handleCollect = () => {
setGatheredResources([]);
}
return (
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 bg-gray-900 p-6 rounded-xl shadow-xl">
{remainingCooldown !== null && (
<div className="col-span-2 md:col-span-3 text-center p-2 bg-yellow-900/30 border-2 border-yellow-500/50 rounded-lg">
<span className="text-yellow-200">Cooldown: {formatCooldownTime(remainingCooldown)}</span>
</div>
)}
<ActionButton
icon={Pizza}
label="Feed"
onClick={performBasicAction('FEED')}
color="green"
disabled={remainingCooldown !== null}
/>
<ActionButton
icon={PlayCircle}
label="Play"
onClick={performBasicAction('PLAY')}
color="blue"
disabled={remainingCooldown !== null}
/>
<ActionButton
icon={Moon}
label="Sleep"
onClick={performBasicAction('SLEEP')}
color="purple"
disabled={remainingCooldown !== null}
/>
<div className="col-span-2 md:col-span-3 grid grid-cols-3 gap-4">
<ActionResourceButton
pet={pet}
icon={FeatherIcon}
label="Gather"
actionType="gather"
color="amber"
onActionClick={() => handleActionStart('gather')}
onActionComplete={handleGatherComplete}
/>
<ActionResourceButton
pet={pet}
icon={Compass}
label="Explore"
actionType="explore"
color="emerald"
onActionClick={() => handleActionStart('explore')}
onActionComplete={handleGatherComplete}
/>
<ActionResourceButton
pet={pet}
icon={Sword}
label="Battle"
actionType="battle"
color="red"
onActionClick={() => handleActionStart('battle')}
onActionComplete={handleGatherComplete}
/>
</div>
<div className="col-span-2 md:col-span-3">
<CollectResourcesButton
petId={pet.id}
resources={gatheredResources}
onCollect={handleCollect}
onPetUpdate={onPetUpdate}
/>
</div>
<ResourceSelectionModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onGather={handleResourceSelect}
pet={pet}
isGathering={pet.petGatherAction !== 'IDLE'}
/>
</div>
);
}