/** * SharedContainerModal — pick roommates for a shared pot/container (Bekky, 2026-09-04). * * A shared container is a physical fact: two plants in one pot cannot be watered * independently. This modal lets the user pick which same-space plants share the * pot (roommates), or mark the roommate as not-yet-added ("Haven't added yet" / * "Not in my garden yet" — a pending slot). * * The modal is opened from the setup form's "Shared container" chip and from the * plant detail's Container section. It writes a `sharedContainerId` (a UUID) to * every selected plant so they all share the same id (symmetric). */ import React, { useMemo, useState } from 'react'; import { Image, Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { dark, green, line } from '../constants/theme'; import type { SavedPlantProfile } from '../types/plantScan'; const EdgeModal = Modal as any; interface SharedContainerModalProps { visible: boolean; /** The plant being edited (the one whose container we're setting). */ plantId: string; /** All saved plants in the SAME space as the plant being edited. */ sameSpacePlants: SavedPlantProfile[]; /** Current sharedContainerId (if any) — plants with this id are roommates. */ currentContainerId?: string; /** Called with the chosen roommate plant ids (the full roommate set, NOT * including the plant being edited). Empty array = standalone (no container). */ onConfirm: (roommateIds: string[]) => void; onCancel: () => void; } export function SharedContainerModal({ visible, plantId, sameSpacePlants, currentContainerId, onConfirm, onCancel, }: SharedContainerModalProps) { // Roommates = same-space plants that already share the current container id. const [selectedIds, setSelectedIds] = useState([]); // Deferred chip: "Haven't added yet" — a pending slot for a roommate that // isn't in the app (or in the same space) yet. Just one option (Bekky, // 2026-09-04) — kept minimal on purpose. const [deferred, setDeferred] = useState(false); // Reset selection each time the modal opens. React.useEffect(() => { if (visible) { const existing = sameSpacePlants .filter(p => p.id !== plantId && p.sharedContainerId && p.sharedContainerId === currentContainerId) .map(p => p.id); setSelectedIds(existing); setDeferred(false); } }, [visible, plantId, sameSpacePlants, currentContainerId]); const toggle = (id: string) => { setSelectedIds(current => current.includes(id) ? current.filter(x => x !== id) : [...current, id]); }; const pickable = useMemo( () => sameSpacePlants.filter(p => p.id !== plantId), [sameSpacePlants, plantId] ); if (!visible) return null; return ( 🪴 Shared container Which plants share this pot? They'll be watered and fed together — you can't care for one without the other. {pickable.length > 0 ? ( <> Plants in this space {pickable.map(p => { const isSelected = selectedIds.includes(p.id); return ( toggle(p.id)} style={[s.roommateRow, isSelected && s.roommateRowSelected]} > {p.photoUri ? ( ) : ( )} {p.commonName || p.name || 'Plant'} {isSelected ? : null} ); })} ) : ( No other plants in this space yet. )} Roommate not in the app yet? setDeferred(v => !v)} style={[s.deferredChip, deferred && s.deferredChipSelected]} > Haven't added yet {deferred ? ( We'll leave a spot for this roommate. When you add them, you can link them into this pot. ) : null} Cancel 0 || deferred) && s.confirmBtnDisabled]} activeOpacity={0.86} disabled={!(selectedIds.length > 0 || deferred)} onPress={() => onConfirm(selectedIds)} > {selectedIds.length > 0 ? `Complete (${selectedIds.length})` : 'Complete'} ); } const s = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(16, 42, 16, 0.55)', justifyContent: 'center', alignItems: 'center', padding: 24, }, card: { width: '100%', maxWidth: 380, maxHeight: '85%', backgroundColor: '#FFFCF5', borderRadius: 24, padding: 20, shadowColor: '#000', shadowOpacity: 0.25, shadowRadius: 20, shadowOffset: { width: 0, height: 8 }, elevation: 12, }, title: { fontSize: 20, fontWeight: '800', color: dark, textAlign: 'center', marginBottom: 6, }, message: { fontSize: 14, color: '#3A382E', textAlign: 'center', lineHeight: 20, marginBottom: 14, }, scroll: { flexGrow: 0, marginBottom: 14, }, scrollContent: { paddingBottom: 4, }, sectionLabel: { fontSize: 12, fontWeight: '700', color: '#5B7553', textTransform: 'uppercase', letterSpacing: 0.5, marginTop: 10, marginBottom: 6, }, roommateRow: { flexDirection: 'row', alignItems: 'center', gap: 10, paddingVertical: 8, paddingHorizontal: 10, borderRadius: 12, borderWidth: 1.5, borderColor: line, backgroundColor: '#FFF', marginBottom: 6, }, roommateRowSelected: { borderColor: green, backgroundColor: '#E8F5D3', }, thumb: { width: 34, height: 34, borderRadius: 17, backgroundColor: '#E8F0E4', }, thumbPlaceholder: { backgroundColor: '#E8F0E4', }, roommateName: { flex: 1, fontSize: 14, fontWeight: '600', color: '#2D4A2D', }, roommateNameSelected: { fontWeight: '800', color: '#1E5E3A', }, check: { fontSize: 16, fontWeight: '900', color: green, }, emptyNote: { fontSize: 13, color: '#7A704D', fontStyle: 'italic', marginBottom: 6, }, deferredRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, }, deferredChip: { borderRadius: 999, borderWidth: 1.5, borderColor: '#B8C4A8', backgroundColor: '#FFF', paddingHorizontal: 12, paddingVertical: 7, }, deferredChipSelected: { borderColor: green, backgroundColor: '#E8F5D3', }, deferredChipText: { fontSize: 13, fontWeight: '600', color: '#2D4A2D', }, deferredChipTextSelected: { fontWeight: '800', color: '#1E5E3A', }, deferredHint: { fontSize: 12, color: '#7A704D', marginTop: 8, lineHeight: 17, }, buttonRow: { flexDirection: 'row', gap: 12, }, cancelBtn: { flex: 1, minHeight: 50, borderRadius: 999, borderWidth: 1.5, borderColor: '#B85C4A', backgroundColor: '#FCE9E6', alignItems: 'center', justifyContent: 'center', paddingHorizontal: 16, }, cancelText: { color: '#8E2F1E', fontSize: 16, fontWeight: '900', }, confirmBtn: { flex: 1, minHeight: 50, borderRadius: 999, backgroundColor: dark, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 16, }, confirmBtnDisabled: { backgroundColor: '#B8C4A8', }, confirmText: { color: '#fff', fontSize: 16, fontWeight: '900', }, });