import React, { useState, useLayoutEffect, useEffect, useRef, useCallback } from 'react'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'; import localForage from 'localforage'; import './Style-light.css'; import './Style-dark.css'; import './Style-black.css'; import './Style-red.css'; import './fonts/Fonts.css'; import { BackwardDot, ForwardDot } from './utils.js'; import Feed from './Feed.js'; import Article from './Article.js'; import Comments from './Comments.js'; import Search from './Search.js'; import Submit from './Submit.js'; import Results from './Results.js'; import ScrollToTop from './ScrollToTop.js'; import Settings from './Settings.js'; function App() { const [theme, setTheme] = useState(localStorage.getItem('theme') || ''); const cache = useRef({}); const [isFullScreen, setIsFullScreen] = useState(!!document.fullscreenElement); const [waitingWorker, setWaitingWorker] = useState(null); const [settingsOpen, setSettingsOpen] = useState(false); const defaultBodyFontSize = 1.0; const [bodyFontSize, setBodyFontSize] = useState(Number(localStorage.getItem('bodyFontSize')) || defaultBodyFontSize); const [bodyFont, setBodyFont] = useState(localStorage.getItem('bodyFont') || 'Sans Serif'); const [articleFont, setArticleFont] = useState(localStorage.getItem('articleFont') || 'Apparatus SIL'); const [filterSmallweb, setFilterSmallweb] = useState(() => localStorage.getItem('filterSmallweb') === 'true'); const [feedSources, setFeedSources] = useState(() => { const saved = localStorage.getItem('feedSources'); return saved ? JSON.parse(saved) : { hackernews: true, reddit: true, lobsters: true, tildes: true, }; }); const updateCache = useCallback((key, value) => { cache.current[key] = value; }, []); useEffect(() => { const onSWUpdate = e => { setWaitingWorker(e.detail.waiting); }; window.addEventListener('swUpdate', onSWUpdate); return () => window.removeEventListener('swUpdate', onSWUpdate); }, []); useEffect(() => { if (Object.keys(cache.current).length === 0) { localForage.iterate((value, key) => { updateCache(key, value); }).then(() => { console.log('loaded cache from localforage'); }); } }, [updateCache]); useEffect(() => { const onFullScreenChange = () => setIsFullScreen(!!document.fullscreenElement); document.addEventListener('fullscreenchange', onFullScreenChange); return () => document.removeEventListener('fullscreenchange', onFullScreenChange); }, []); useLayoutEffect(() => { if (theme === 'dark') { document.body.style.backgroundColor = '#1a1a1a'; } else if (theme === 'black') { document.body.style.backgroundColor = '#000'; } else if (theme === 'red') { document.body.style.backgroundColor = '#000'; } else { document.body.style.backgroundColor = '#eeeeee'; } }, [theme]); useEffect(() => { document.documentElement.style.fontSize = `${bodyFontSize}rem`; }, [bodyFontSize]); const fontMap = { 'Sans Serif': 'sans-serif', 'Serif': 'serif', 'Apparatus SIL': "'Apparatus SIL', sans-serif" }; useEffect(() => { document.body.style.fontFamily = fontMap[bodyFont]; }, [bodyFont]); useEffect(() => { const styleId = 'article-font-family-style'; let style = document.getElementById(styleId); if (!style) { style = document.createElement('style'); style.id = styleId; document.head.appendChild(style); } style.innerHTML = `.story-text { font-family: ${fontMap[articleFont]} !important; }`; }, [articleFont]); return (
QotNews
Hacker News, Reddit, Lobsters, and Tildes articles rendered in reader mode.