import React, {useEffect, useRef, useState} from 'react'; import AddPhotoModal from './AddPhotoModal'; import GardenFormModal from './GardenFormModal'; import EditPlantingModal from './EditPlantingModal'; import GardenCard from './GardenCard'; import MarkFinishedModal from './MarkFinishedModal'; import PlantSomethingModal from './PlantSomethingModal'; import RecordHarvestModal from './RecordHarvestModal'; import SaveSeedsModal from './SaveSeedsModal'; import {GARDEN_CARD_CHANGED} from '../nurseryPanel'; const plantingIds = (garden) => [...garden.perennials, ...garden.annuals].map((planting) => planting.id); // The list of garden cards on the gardens index. Props come from // GardensHelper#garden_cards_props. It owns the cards' state so that saving a // planting from the "Add planting" dialog can swap in the garden's // updated card, and the new planting appears without a page reload. export default function GardenCards({gardens: initialGardens, default_icon_url: defaultIconUrl, spade_icon_url: spadeIconUrl, harvest_icon_url: harvestIconUrl, seed_icon_url: seedIconUrl, photo_icon_url: photoIconUrl, finish_icon_url: finishIconUrl}) { const [gardens, setGardens] = useState(initialGardens); const [plantingIn, setPlantingIn] = useState(null); // the garden the dialog is open for const [editing, setEditing] = useState(null); // the planting the edit dialog is open for const [harvesting, setHarvesting] = useState(null); // the planting the harvest dialog is open for const [savingSeeds, setSavingSeeds] = useState(null); // the planting the save seeds dialog is open for const [addingPhoto, setAddingPhoto] = useState(null); // {label, href}: what the add photo dialog is open for const [finishing, setFinishing] = useState(null); // the planting the mark as finished dialog is open for const [editingGarden, setEditingGarden] = useState(null); // the garden the edit dialog is open for const [notice, setNotice] = useState(null); const [highlight, setHighlight] = useState(null); // {id, kind}: the planting just added or harvested const highlightTimer = useRef(null); // The "jump to" links point at #garden-, but the cards only exist once // React has mounted, after the browser has already tried to scroll. useEffect(() => { if (!window.location.hash) return; const target = document.getElementsByName(window.location.hash.slice(1))[0]; if (target) target.scrollIntoView(); }, []); useEffect(() => () => clearTimeout(highlightTimer.current), []); // Something saved elsewhere on the page (a seedling transplanted into one of // these gardens) sends the garden's new card, so it shows without a reload. useEffect(() => { const changed = (event) => replaceCard(event.detail); window.addEventListener(GARDEN_CARD_CHANGED, changed); return () => window.removeEventListener(GARDEN_CARD_CHANGED, changed); }, []); // The response leaves out the owner (the page already has it); keep ours. function replaceCard(updatedCard) { setGardens((current) => current.map((garden) => (garden.id === updatedCard.id ? {...updatedCard, owner: garden.owner} : garden))); } function created(updatedCard, crop) { const before = gardens.find((garden) => garden.id === updatedCard.id); const known = new Set(plantingIds(before)); const added = plantingIds(updatedCard).find((id) => !known.has(id)); replaceCard(updatedCard); setPlantingIn(null); setNotice(`Planted ${crop ? crop.name : 'something'} in ${updatedCard.name}.`); flash(added, 'added'); } // Picks out a planting for a few seconds, so you can see which one changed. function flash(id, kind) { setHighlight({id, kind}); clearTimeout(highlightTimer.current); highlightTimer.current = setTimeout(() => setHighlight(null), 5000); } function edited(updatedCard, planting) { replaceCard(updatedCard); setEditing(null); setNotice(`Saved changes to ${planting.crop.name}.`); } function harvested(updatedCard, planting) { replaceCard(updatedCard); setHarvesting(null); setNotice(`Recorded a harvest of ${planting.crop.name} in ${updatedCard.name}.`); flash(planting.id, 'harvested'); } function seedsSaved(seed, planting) { setSavingSeeds(null); setNotice(<>Saved {planting.crop.name} seeds to your stash. See them.); } function photoAdded(result, target) { if (result.garden) replaceCard(result.garden); // a garden's picture may now be this one setAddingPhoto(null); setNotice(<>Added a photo to {target.label}. See it.); } function finished(updatedCard, planting) { replaceCard(updatedCard); // the planting is no longer in progress, so it leaves the card setFinishing(null); setNotice(`Marked ${planting.crop.name} as finished.`); } // Saved from the garden's edit dialog. A garden marked inactive is not on this // list, so it leaves it, and its plantings with it. function gardenEdited(updatedCard) { setEditingGarden(null); if (updatedCard.active) { replaceCard(updatedCard); setNotice(`Saved changes to ${updatedCard.name}.`); } else { setGardens((current) => current.filter((garden) => garden.id !== updatedCard.id)); setNotice(`${updatedCard.name} is now inactive, and its plantings are finished. You will find it under inactive gardens.`); } } return ( <> {notice && (