import React, {useEffect, useRef, useState} from 'react';
import ActionsMenu from './ActionsMenu';
import AddPhotoModal from './AddPhotoModal';
import EditPlantingModal from './EditPlantingModal';
import MarkFinishedModal from './MarkFinishedModal';
import RecordHarvestModal from './RecordHarvestModal';
import SaveSeedsModal from './SaveSeedsModal';
// The menu items we open a dialog for. Anything else — delete, and whatever is
// added later — keeps its link, its data-method and its data-confirm.
const DIALOGS = ['edit', 'harvest', 'seeds', 'finish', 'photo'];
// Transplanting is a server-rendered Bootstrap dialog, already on the page but
// hidden. Its form posts as it always has, so it needs no JSON endpoint.
function showTransplantDialog() {
const dialog = document.getElementById('transplant-modal');
if (dialog && window.bootstrap) window.bootstrap.Modal.getOrCreateInstance(dialog).show();
}
// The quiet "add" on each section heading opens the same dialog as the matching
// menu item. Those links are server-rendered, outside this island, so we listen
// for them on the document; their href stays as the fallback without
// JavaScript. The handler is held in a ref so the listener is attached once
// rather than on every render.
function useSectionTriggers(onTrigger) {
const latest = useRef(onTrigger);
latest.current = onTrigger;
useEffect(() => {
function onClick(event) {
const trigger = event.target.closest('[data-planting-dialog]');
if (!trigger) return;
event.preventDefault();
latest.current(trigger.dataset.plantingDialog, trigger.getAttribute('href'));
}
document.addEventListener('click', onClick);
return () => document.removeEventListener('click', onClick);
}, []);
}
// The one actions menu for a planting's own page: the three-dot button in the
// top right of the hero, and the dialogs its items open.
//
// The items come from the server (label, href, and for non-GET links `method`
// and `confirm`), so permissions and wording stay in one place.
//
// Saving reloads the page, because the sections below (facts, harvests, seeds,
// photos) are server-rendered and there is no JSON for them yet. The member
// stays on the planting either way, which is the point; re-rendering only the
// affected section is tracked separately.
export default function PlantingActions({
planting, actions,
harvest_icon_url: harvestIconUrl, seed_icon_url: seedIconUrl,
photo_icon_url: photoIconUrl, finish_icon_url: finishIconUrl
}) {
// {kind, href}: which dialog is open, and the link it was opened from, which
// the add-photo dialog needs.
const [dialog, setDialog] = useState(null);
const open = (kind, href) => setDialog({kind, href});
const close = () => setDialog(null);
// Close the dialog, then reload on the next tick. Reloading straight from the
// save callback tears the page down while the dialog is still finishing its
// own update, which leaves the browser wedged mid-navigation.
const saved = () => {
setDialog(null);
setTimeout(() => window.location.reload(), 0);
};
useSectionTriggers(open);
// After the hooks: React counts them, and an early return above would change
// the count between renders.
if (!actions || actions.length === 0) return null;
function choose(action, event) {
if (action.key === 'transplant') {
event.preventDefault();
showTransplantDialog();
} else if (DIALOGS.includes(action.key)) {
event.preventDefault();
open(action.key, action.href);
}
}
const kind = dialog && dialog.kind;
return (
<>
}
onSelect={choose}
/>
{kind === 'edit' && (
)}
{kind === 'harvest' && (
)}
{kind === 'seeds' && (
)}
{kind === 'finish' && (
)}
{kind === 'photo' && (
)}
>
);
}