import React, { useState, useEffect, useRef } from 'react'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'; import { useScroll, useRafState } from 'react-use'; import qs from 'qs'; import svg4everybody from 'svg4everybody'; import Icon from './ui/Icon'; import { Discover, Discussions, Blog, Tag, Home } from './ui/Feeds'; import { Friends, Readers } from './ui/Users'; import Settings from './ui/Settings'; import Contacts from './ui/Contacts'; import Chat from './ui/Chat'; import Header from './ui/Header'; import Post from './ui/Post'; import Thread from './ui/Thread'; import Login from './ui/Login'; import cookie from 'react-cookies'; import { me } from './api'; const elClassHidden = 'header--hidden'; const elClassFull = 'content--full'; export default function App() { let contentRef = useRef(null); useEffect(() => { svg4everybody(); }, []); let params = qs.parse(window.location.search.substring(1)); if (params.hash) { cookie.save('hash', params.hash, { path: '/' }); let retpath = params.retpath || `${window.location.protocol}//${window.location.host}${window.location.pathname}`; window.history.replaceState({}, document.title, retpath); } /** * @type {import('./api').SecureUser} */ const unknownUser = { uid: -1 }; const [visitor, setVisitor] = useState(unknownUser); let updateStatus = () => { // refresh server visitor state (unread counters) me().then(visitor => { setVisitor(visitor); }); }; const [scrollState, setScrollState] = useRafState({ hidden: false, bottom: false, prevScroll: 0 }); let { x, y } = useScroll(contentRef); useEffect(() => { let dHeight = contentRef.current.scrollHeight; let wHeight = contentRef.current.clientHeight; setScrollState((scrollState) => { let wScrollDiff = scrollState.prevScroll - y; let hidden = scrollState.hidden; let bottom = scrollState.bottom; if (y <= 0) { // scrolled to the very top; element sticks to the top hidden = false; bottom = false; } else if ((wScrollDiff > 0) && hidden) { // scrolled up; element slides in hidden = false; bottom = false; } else if (wScrollDiff < 0) { // scrolled down if ((y + wHeight) >= dHeight && hidden) { // scrolled to the very bottom; element slides in hidden = false; bottom = true; } else { // scrolled down; element slides out hidden = true; bottom = false; } } return { hidden: hidden, bottom: bottom, prevScroll: y }; }); }, [x, y, setScrollState]); const [hash, setHash] = useState(cookie.load('hash')); const [eventSource, setEventSource] = useState({}); useEffect(() => { let es; const anonymousUser = { uid: 0 }; if (hash) { me().then(visitor => auth(visitor)) .catch(() => setVisitor(anonymousUser)); if ('EventSource' in window) { const eventParams = { hash: hash }; let url = new URL(`https://juick.com/api/events?${qs.stringify(eventParams)}`); es = new EventSource(url.toString()); es.onopen = () => { console.log('online'); }; es.onerror = () => { es.removeEventListener('read', updateStatus); es.removeEventListener('msg', updateStatus); }; es.addEventListener('read', updateStatus); es.addEventListener('msg', updateStatus); setEventSource(es); } } else { setVisitor(anonymousUser); } return (() => { if (es && es.removeEventListener) { es.removeEventListener('read', updateStatus); es.removeEventListener('msg', updateStatus); } }); }, [hash]); /** * @param {{ pathname: any; search: string; }[]} history * @param {any} pathname * @param {any} searchString */ let search = (history, pathname, searchString) => { let location = {}; location.pathname = pathname; location.search = `?search=${searchString}`; history.push(location); }; /** * @param {import("./api").SecureUser} visitor */ let auth = (visitor) => { setVisitor(prevState => { if (visitor.hash != prevState.hash) { setHash(visitor.hash); } return visitor; }); }; return ( <>
} /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } />
{ visitor.uid > 0 && }
); }