import { useState, useEffect, Fragment } from 'react'; import { useNavigate } from 'react-router'; import { Typography, CircularProgress, DialogContent, DialogTitle, Stack, Card, CardContent, DialogActions, Button, Chip, FormGroup, FormControlLabel, Switch } from '@mui/material'; import { Hash, Resources, Identity, Space, SpaceEntryPoint, MutationEvent, MutableContentEvents } from '@hyper-hyper-space/core'; import { useObjectDiscoveryIfNecessary, useObjectState } from '@hyper-hyper-space/react'; import { Home, SpaceLink } from '@hyper-hyper-space/home'; import { Box } from '@mui/system'; import { Profile } from '@hyper-hyper-space/home'; import { HyperBrowserConfig } from '../../model/HyperBrowserConfig'; import { TextSpace } from '../../model/text/TextSpace'; import { WikiSpace } from '@hyper-hyper-space/wiki-collab'; import HomeItem from '../../pages/home/components/HomeItem'; function ViewProfile(props: {identityHash: Hash, close: () => void, home?: Home, resources?: Resources, resourcesForDiscovery?: Resources, anonMode?: boolean, startChat?: (id: Identity) => void}) { const navigate = useNavigate(); const [locallyFoundIdentity, setLocallyFoundIdentity] = useState(); const identity = useObjectDiscoveryIfNecessary(props.resourcesForDiscovery, props.identityHash, locallyFoundIdentity); const contactsState = useObjectState(props.home?.contacts?.current); const hostingState = useObjectState(props.home?.contacts?.hosting); const [profile, setProfile] = useState(); const profileState = useObjectState(profile); const [hashForDiscovery, setHashForDiscovery] = useState(); const discoveredProfile = useObjectDiscoveryIfNecessary(props.resourcesForDiscovery, hashForDiscovery); useEffect(() => { let tearDown: (() => void) | undefined = undefined; let discovered = false; const configDiscovered = async () => { if (!discovered && profile === undefined && discoveredProfile !== undefined) { discovered = true; let p = discoveredProfile.clone(); if (props.anonMode || !contactsState?.getValue()?.has(p)) { const tmpResources = await HyperBrowserConfig.initTransientSpaceResources(hashForDiscovery as Hash); p.setResources(tmpResources); await tmpResources.store.save(p); await p.loadAndWatchForChanges(); await p.startSync({requester: tmpResources.getId()}); p.published?.addObserver( (ev: MutationEvent) => { if (ev.emitter === p.published) { if (ev.action === MutableContentEvents.AddObject) { const link = ev.data as SpaceLink; link.name?.loadAndWatchForChanges(); } else if (ev.action === MutableContentEvents.RemoveObject) { const link = ev.data as SpaceLink; link.name?.dontWatchForChanges(); } } }); for (const link of p.published?.values() || []) { link.name?.loadAndWatchForChanges(); } tearDown = async () => { p.dontWatchForChanges(); await p.stopSync(); tmpResources.mesh.shutdown(); tmpResources.store.close(); }; } setProfile(p); } }; configDiscovered(); return () => { if (tearDown !== undefined) { tearDown(); } }; }, [hashForDiscovery, discoveredProfile]); useEffect(() => { const attemptToLoad = async () => { if (props.identityHash !== undefined && props.resources !== undefined) { const id = await props.resources.store.load(props.identityHash, false); if (id !== undefined && id instanceof Identity) { setLocallyFoundIdentity(id); } } }; attemptToLoad(); }, [props.identityHash, props.resources]); useEffect(() => { // FIXME: use contacts instead of attempting to load profile. const init = async () => { if (identity !== undefined && (props.resources !== undefined || props.anonMode)) { let p = new Profile(identity); const profileHash = p.hash(); const loaded = await props.resources?.store.load(profileHash, false) as Profile|undefined; if (loaded !== undefined) { p = loaded; p.published?.addObserver( (ev: MutationEvent) => { if (ev.emitter === p.published) { if (ev.action === MutableContentEvents.AddObject) { const link = ev.data as SpaceLink; link.name?.loadAndWatchForChanges(); } else if (ev.action === MutableContentEvents.RemoveObject) { const link = ev.data as SpaceLink; link.name?.dontWatchForChanges(); } } }); await p.startSync(); setProfile(p); for (const link of p.published?.values() || []) { link.name?.loadAndWatchForChanges(); } return () => { p.stopSync(); } } else { console.log('will try to fetch profile ' + profileHash) setHashForDiscovery(profileHash); } } }; init(); }, [identity, props.resources]); const contact = profileState?.value !== undefined && (contactsState?.value?.has(profileState.value) || false); const [hosting, setHosting] = useState(false); useEffect(() => { setHosting(identity !== undefined && (hostingState?.value?.has(identity) || false)); }, [identity, hostingState]); const you = profile?.owner?.getLastHash() === props.home?.getAuthor()?.getLastHash(); const removeFromContacts = async () => { if (profile !== undefined) { await props.home?.contacts?.current?.delete(profile); await props.home?.contacts?.current?.saveQueuedOps(); } }; const addToContacts = async () => { if (profile !== undefined) { await props.home?.contacts?.current?.add(profile); await props.home?.contacts?.current?.saveQueuedOps(); } }; const toggleHosting = async () => { if (hosting) { await removeFromHosting(); } else { await addToHosting(); if (profile !== undefined && !props.home?.contacts?.current?.has(profile)) { await props.home?.contacts?.current?.add(profile); await props.home?.contacts?.current?.saveQueuedOps(); } } } const removeFromHosting = async () => { if (identity !== undefined) { await props.home?.contacts?.hosting?.delete(identity); await props.home?.contacts?.hosting?.saveQueuedOps(); } }; const addToHosting = async () => { if (identity !== undefined) { await props.home?.contacts?.hosting?.add(identity); await props.home?.contacts?.hosting?.saveQueuedOps(); } }; return { (identity === undefined || contactsState === undefined) && Loading profile... } { identity !== undefined && contactsState !== undefined &&
{identity.info?.name}
{!props.anonMode && {contact && !you && } size="small" color="success" label="Contact" onDelete={removeFromContacts}/> } {!contact && !you && } {you && } }
{!props.anonMode && !you && props.startChat !== undefined && } { you && }
{ profileState?.value?.getPictureDataUrl() === undefined &&
No picture
} { profileState?.value?.getPictureDataUrl() !== undefined && } {identity?.info !== undefined && {Object.entries(identity.info).map((entry:[string, any]) => { return {entry[0]}: {entry[1]}
; }) } code: {Space.getWordCodingForHash(identity.getLastHash() as Hash).join(' ')}
}
{ profileState?.value?.about?._value !== undefined && About {profileState?.value?.about?._value} } {!props.anonMode && } label={'Help ' + identity.info?.name + ' keep his published spaces online'}> } {profileState?.value?.published?.size() === 0 && This person is not publishing any spaces. } {profileState?.value?.published !== undefined && profileState?.value?.published?.size() > 0 && {Array.from(profileState?.value?.published.values() || []).map((item: SpaceLink) => { const entry = item.spaceEntryPoint as any as SpaceEntryPoint; const name = item.name; let icon = ""; let title: (string | undefined) = undefined; if (item.spaceEntryPoint instanceof TextSpace) { title = 'Text File'; icon = 'streamline-icon-pencil-write-1@48x48.png'; } else if (item.spaceEntryPoint instanceof WikiSpace) { title = 'Wiki'; icon = 'streamlinehq-book-edit-content-48.png'; } return { window.open('./#/space/' + encodeURIComponent(item.spaceEntryPoint?.getLastHash() as Hash), '_blank') }} menu={[ {name: 'Open', action: () => { window.open('./#/space/' + encodeURIComponent(item.spaceEntryPoint?.getLastHash() as Hash), '_blank') }} ]} title={title} dense={true} />; })} }
} { !props.anonMode && profile !== undefined && !contact && profile.owner?.getLastHash() !== props.home?.getAuthor()?.getLastHash() && }
; } export default ViewProfile;