import { useState, useEffect, useRef } from 'react'; import { useNavigate, useOutletContext } from 'react-router'; import { Stack, Dialog, DialogContent, DialogTitle, useTheme, useMediaQuery, Tabs, Tab, Card, CardContent, Button, DialogActions, TextField, InputAdornment, Typography, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Avatar, Divider, Chip } from '@mui/material'; import { Box } from '@mui/system'; import { HomeContext } from '../HomeSpace'; import { useObjectState } from '@hyper-hyper-space/react'; import { Hash } from '@hyper-hyper-space/core'; import { Contact, ProfileUtils } from '../../../model/ProfileUtils'; import { Home } from '@hyper-hyper-space/home'; type LetterIndexEntry = { letter: string, hash?: Hash } function ContactsDialog({home}:{ home?: Home | undefined}) { const navigate = useNavigate(); const [open, setOpen] = useState(true); const theme = useTheme(); const fullScreen = useMediaQuery(theme.breakpoints.down('md')); const close = () => { setOpen(false); navigate('..'); }; const contextHome = useOutletContext()?.home; home = home || contextHome const contactsState = useObjectState(home?.contacts?.current); const handleChangeTab = () => { }; const [searchValue, setSearchValue] = useState(''); const searchValueChanged = (e: React.ChangeEvent) => { const newValue = e.currentTarget.value; setSearchValue(newValue); }; const searchKeyUp = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { setSearchValue(''); } } const [contacts, setContacts] = useState>([]); const [letters, setLetters] = useState>([]); const [currentLetter, setCurrentLetter] = useState(); const contactElements = useRef({} as any); const [scrollTimeout, setScrollTimeout] = useState(undefined); useEffect(() => { const initials = (name?: string) => { return (name || '').split(' ').filter((s: string) => s.length > 0).map((s: string) => s[0].toUpperCase()).join('').slice(0, 3); }; const load = async () => { if (contactsState?.value !== undefined) { const keywords = ProfileUtils.normalizeStringForKeywordSearch(searchValue).split(/[ -]+/); const contactProfiles = Array.from(contactsState.value._elements?.values()); const p = home?.profile; if (p !== undefined) { contactProfiles.push(p); } const cs = contactProfiles.map(ProfileUtils.createContact).filter((c: Contact) => ProfileUtils.filterContactForKeywordSearch(c, keywords)); //for (let i=0; i<20; i++) { // cs.push({hash: '0_' + i, code: 'pepa pig plush', name: 'just testing', initials: 'JT', order: 'just testing'}); //} cs.sort((a: {order: string}, b: {order: string}) => a.order.localeCompare(b.order)); let letter = ''; const letterIndexEntries: Array = []; const possibleLetters = new Set(); const allLetters: Array = [] for (let i='A'.charCodeAt(0); i<='Z'.charCodeAt(0); i++) { allLetters.push(String.fromCharCode(i)); } let nextLetter = 0; let firstLetter = 'A'; for (const c of cs) { const normalizedName = ProfileUtils.normalizeStringForKeywordSearch(c.name); if (normalizedName.length > 0) { const newLetter = normalizedName[0].toUpperCase(); if (letter !== newLetter) { if (letter === '') { firstLetter = newLetter; } while (nextLetter < allLetters.length && allLetters[nextLetter].localeCompare(newLetter) < 0) { letterIndexEntries.push({letter: allLetters[nextLetter]}); nextLetter = nextLetter + 1; } if (nextLetter < allLetters.length && allLetters[nextLetter].localeCompare(newLetter) === 0) { nextLetter = nextLetter + 1; } letter = newLetter; possibleLetters.add(letter); letterIndexEntries.push({letter: letter, hash: c.hash}) c.isFirstForLetter = letter; } } } while (nextLetter < allLetters.length) { letterIndexEntries.push({letter: allLetters[nextLetter]}); nextLetter = nextLetter + 1; } setContacts(cs); setLetters(letterIndexEntries); if (currentLetter === undefined || !possibleLetters.has(currentLetter)) { setCurrentLetter(firstLetter); } } const cs = contactsState?.getValue() }; load(); }, [contactsState, searchValue]); //const sx = fullScreen? {} : {maxHeight: '80%'} const share = () => { navigate('../share-profile'); }; const refreshLetterIndex = () => { for (const idx of letters) { if (idx.hash !== undefined) { let elmt = contactElements.current[idx.hash]; var rect = elmt.getBoundingClientRect(); if ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */ rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ ) { setCurrentLetter(idx.letter); break; } } } } const onScroll = () => { if (scrollTimeout === undefined) { window.setTimeout(refreshLetterIndex, 200); } } return (
Contacts
🔎 ), }} value={searchValue} onChange={searchValueChanged} onKeyUp={searchKeyUp} /> {letters.map((idx: LetterIndexEntry) => ( ) => {e.preventDefault(); setCurrentLetter(idx.letter); if (idx.hash !== undefined) { contactElements.current[idx.hash].scrollIntoView(); } } } /> ))} { navigate('../add-contact'); }}> + {contacts.map((c: Contact) => ( { contactElements.current[c.hash] = instance;}} secondaryAction={c.hash !== home?.getAuthor()?.getLastHash() ? : undefined} > { navigate(c.hash === home?.getAuthor()?.getLastHash() ? '../edit-profile' : '../view-profile/' + encodeURIComponent(c.hash)) }}> {c.picture !== undefined && } {c.picture === undefined && {c.initials} } {c.name}{c.hash === home?.getAuthor()?.getLastHash() && }} secondary={c.code} /> ))}
); } export default ContactsDialog;