import { findIndex, get, last } from 'lodash' import Error from 'next/error' import { useRouter } from 'next/router' import React, { useEffect, useState } from 'react' import { FastForward, Lock, Rewind, SkipBack, SkipForward, Tag, Unlock, } from 'react-feather' import { Col, Row, Spinner } from 'reactstrap' import { mutate } from 'swr' import AuthPrompt from '../../components/auth/AuthPrompt' import { Fit, Rename, Search, ShowAll, } from '../../components/mindmap/action-items' import MindMap from '../../components/mindmap/MindMap' import ToolTippedButton from '../../components/mindmap/ToolTippedButton' import { useUser } from '../../utils/auth/useUser' import useFetch from '../../utils/useFetch' const Page = () => { const { user } = useUser() const router = useRouter() const [timestamp, setTimestamp] = useState( typeof window === 'undefined' ? null : parseFloat(new URLSearchParams(location.search).get('timestamp')) ) const { key } = router.query const { data, error } = useFetch( user ? user : null, `/api/mindmaps/${key}?timestamp=${timestamp || ''}` ) const { data: edata, error: eerror } = useFetch( user ? user : null, `/api/timeline/events?key=${key}` ) const [title, setTitle] = useState(key) useEffect(() => { if (user) { mutate( [`/api/mindmaps/${key}?timestamp=${timestamp || ''}`, user.token], null, true ) } }, [user, timestamp, key]) useEffect(() => { if (user) { mutate([`/api/timeline/events?key=${key}`, user.token], null, true) } }, [user, key]) useEffect(() => { if (data && data.ok) { setTitle(data.data.meta.name) } }, [data]) useEffect(() => { const handleRouteChange = (url) => { const fullURL = new URL(url, location.origin) const toTs = fullURL.searchParams.get('timestamp') const toTsF = parseFloat(toTs) || null if ((!toTsF && timestamp) || toTsF !== timestamp) { setTimestamp(toTsF) } } router.events.on('routeChangeComplete', handleRouteChange) return () => { router.events.off('routeChangeComplete', handleRouteChange) } }, [router.events, timestamp]) if (typeof user === 'undefined') { return <Spinner /> } if (error && window.notify) { const options = { place: 'tr', message: 'Failed to fetch mind map!', type: 'danger', autoDismiss: 7, } window.notify(options) } if (eerror && window.notify) { const options = { place: 'tr', message: 'Failed to fetch events!', type: 'danger', autoDismiss: 7, } window.notify(options) } const gotEventData = !eerror && edata && edata.ok const cEvents = gotEventData && edata.data const prevDisabled = !gotEventData || timestamp === cEvents[0].lctime const nextDisabled = !gotEventData || timestamp === last(cEvents).lctime async function jump(to) { if (to === 'now') { await router.push('/mmaps/[key]', `/mmaps/${key}`, { shallow: true }) setTimestamp(null) } else if (gotEventData) { let toTS, idx switch (to) { case 'first': toTS = cEvents[0].lctime break case 'prev': idx = timestamp ? findIndex(cEvents, { lctime: timestamp }) : cEvents.length toTS = cEvents[idx - 1].lctime break case 'next': idx = timestamp ? findIndex(cEvents, { lctime: timestamp }) : cEvents.length - 2 toTS = cEvents[idx + 1].lctime break case 'last': toTS = last(cEvents).lctime break default: toTS = to } await router.push( '/mmaps/[key]', { pathname: `/mmaps/${key}`, query: { timestamp: toTS }, }, { shallow: true } ) setTimestamp(toTS) } } if (user) { const output = [ <Row key="title"> <Col xs="auto" md={7}> <h3> {title} {timestamp ? ( <> <small className={'text-muted'}> {' '} @ {new Date(timestamp * 1000).toLocaleString()} </small> </> ) : null} </h3> </Col> <Col xs="auto" md={5} className={'text-right'}> <ShowAll /> <Fit /> <Search /> | <ToolTippedButton className="ml-1" outline color="secondary" id="tag" disabled={true} tooltip="Tag (Coming Soon)" > <Tag size={16} /> </ToolTippedButton> <ToolTippedButton className="ml-1" outline color="secondary" id="first" disabled={prevDisabled} tooltip="First" onClick={() => jump('first')} > <SkipBack size={16} /> </ToolTippedButton> <ToolTippedButton className="ml-1" outline color="secondary" id="prev" disabled={prevDisabled} tooltip="Previous" onClick={() => jump('prev')} > <Rewind size={16} /> </ToolTippedButton> <ToolTippedButton className="ml-1" outline color="secondary" id="next" disabled={nextDisabled} tooltip="Next" onClick={() => jump('next')} > <FastForward size={16} /> </ToolTippedButton> <ToolTippedButton className="ml-1" outline color="secondary" id="last" disabled={nextDisabled} tooltip="Last" onClick={() => jump('last')} > <SkipForward size={16} /> </ToolTippedButton> | <Rename nameChangedCallBack={setTitle} disabled={!!timestamp} rootNode={get(data, ['data', 'meta'], {})} /> <ToolTippedButton className="ml-1" outline color={timestamp ? 'secondary' : 'danger'} id="now" tooltip={timestamp ? 'Click to unlock' : 'Click to lock'} onClick={() => jump(timestamp ? 'now' : 'last')} > {timestamp ? <Lock size={16} /> : <Unlock size={16} />} </ToolTippedButton> </Col> </Row>, ] if (error && data) { output.push( <Row key="content"> <Col> <Error statusCode={data.status} /> </Col> </Row> ) } else if (eerror && edata) { output.push( <Row key="content"> <Col> <Error statusCode={edata.status} /> </Col> </Row> ) } else { output.push( <Row key="content"> <Col> <MindMap data={data} edata={edata} timestamp={timestamp} jump={jump} /> </Col> </Row> ) } return output } return <AuthPrompt /> } export default Page