2023-06-07 21:24:17 +02:00
import React , { useEffect } from 'react' ;
2023-03-20 17:08:05 +01:00
import { Route , Routes , useLocation } from 'react-router-dom' ;
2023-02-01 13:30:32 +01:00
import { Helmet } from 'react-helmet-async' ;
2023-04-09 20:55:55 +02:00
import 'react-tooltip/dist/react-tooltip.css' ;
import 'react-toastify/dist/ReactToastify.css' ;
2023-04-08 13:27:17 +02:00
import useGeneralStore from './hooks/stores/useGeneralStore' ;
2023-04-29 18:01:19 +02:00
import { GlobalStyle } from './components/styled/GlobalStyle.styled' ;
import { Toast } from './components/styled/Toast.styled' ;
2023-05-16 16:37:50 +02:00
import All from './components/views/All' ;
import AllCatalog from './components/views/AllCatalog' ;
2023-03-19 11:49:05 +01:00
import Board from './components/views/Board' ;
import Catalog from './components/views/Catalog' ;
2023-06-27 21:06:30 +02:00
import Description from './components/views/Description' ;
2023-05-03 11:01:41 +02:00
import Home from './components/views/Home' ;
2023-03-19 11:49:05 +01:00
import NotFound from './components/views/NotFound' ;
2023-04-10 10:51:16 +02:00
import Pending from './components/views/Pending' ;
2023-06-27 21:06:30 +02:00
import Rules from './components/views/Rules' ;
2023-05-03 11:01:41 +02:00
import Subscriptions from './components/views/Subscriptions' ;
2023-05-07 17:48:38 +02:00
import SubscriptionsCatalog from './components/views/SubscriptionsCatalog' ;
2023-05-03 11:01:41 +02:00
import Thread from './components/views/Thread' ;
2023-05-24 12:15:43 +02:00
import CaptchaModal from './components/modals/CaptchaModal' ;
2023-03-22 16:23:32 +01:00
import { importAll } from './components/ImageBanner' ;
2023-04-09 20:55:55 +02:00
import preloadImages from './utils/preloadImages' ;
2023-05-16 13:26:05 +02:00
import useError from "./hooks/useError" ;
2023-09-10 12:20:31 +02:00
import useInfo from "./hooks/useInfo" ;
2023-05-16 13:26:05 +02:00
import useSuccess from "./hooks/useSuccess" ;
2023-09-10 12:20:31 +02:00
import packageJson from '../package.json' ;
2023-09-10 15:21:03 +02:00
const commitRef = process ? . env ? . REACT_APP_COMMIT_REF || '' ;
2023-02-08 21:49:36 +01:00
2023-03-13 14:29:34 +01:00
export default function App () {
2023-03-20 17:08:05 +01:00
const {
bodyStyle , setBodyStyle ,
2023-06-22 18:12:15 +02:00
setDefaultSubplebbits ,
2023-04-09 20:55:55 +02:00
isCaptchaOpen , setIsCaptchaOpen ,
2023-03-20 17:08:05 +01:00
setIsSettingsOpen ,
2023-04-09 20:55:55 +02:00
selectedStyle , setSelectedStyle ,
2023-03-20 17:08:05 +01:00
setShowPostForm ,
setShowPostFormLink ,
2023-04-08 13:27:17 +02:00
} = useGeneralStore ( state => state );
2023-03-20 17:08:05 +01:00
const location = useLocation ();
2023-03-21 11:17:54 +01:00
const isHomeRoute = location . pathname === "/" ;
2023-09-10 16:13:33 +02:00
const isElectron = window . electron && window . electron . isElectron ;
2023-04-29 14:39:57 +02:00
2023-06-14 16:24:25 +02:00
const [, setNewErrorMessage ] = useError ();
const [, setNewSuccessMessage ] = useSuccess ();
2023-09-10 12:20:31 +02:00
const [, setNewInfoMessage ] = useInfo ();
2023-05-16 13:26:05 +02:00
useEffect (() => {
const successToast = localStorage . getItem ( "successToast" );
const errorToast = localStorage . getItem ( "errorToast" );
if ( successToast ) {
2023-06-14 16:24:25 +02:00
setNewSuccessMessage ( successToast );
2023-05-16 13:26:05 +02:00
localStorage . removeItem ( "successToast" );
} else if ( errorToast ) {
2023-06-14 16:24:25 +02:00
setNewErrorMessage ( errorToast );
2023-05-16 13:26:05 +02:00
localStorage . removeItem ( "errorToast" );
} else {
return ;
}
2023-09-11 12:53:05 +02:00
}, [ setNewErrorMessage , setNewSuccessMessage ]);
2023-09-10 12:20:31 +02:00
// check for new version
2023-09-11 12:29:22 +02:00
// TODO: delete this code and toast when v1.0.0 is released
2023-09-10 12:20:31 +02:00
useEffect (() => {
const fetchVersionInfo = async () => {
try {
const packageRes = await fetch ( 'https://raw.githubusercontent.com/plebbit/plebchan/master/package.json' , { cache : 'no-cache' });
const packageData = await packageRes . json ();
2023-09-10 15:21:03 +02:00
2023-09-10 12:20:31 +02:00
if ( packageJson . version !== packageData . version ) {
2023-09-10 16:13:33 +02:00
const newVersionInfo = isElectron
2023-09-11 12:53:05 +02:00
? `New version available, plebchan v ${ packageData . version } . You are using v ${ packageJson . version } Go to github.com/plebbit/plebchan/releases/latest to download the latest version.`
: `New version available, plebchan v ${ packageData . version } . You are using v ${ packageJson . version } . Refresh the page to update.` ;
setNewInfoMessage ( newVersionInfo );
2023-09-10 15:21:03 +02:00
}
if ( commitRef . length > 0 ) {
2023-09-11 10:27:18 +02:00
const commitRes = await fetch ( 'https://api.github.com/repos/plebbit/plebchan/commits?per_page=1&sha=development' , { cache : 'no-cache' });
const commitData = await commitRes . json ();
2023-09-10 15:21:03 +02:00
2023-09-11 10:27:18 +02:00
const latestCommitHash = commitData [ 0 ]. sha ;
if ( latestCommitHash . trim () !== commitRef . trim ()) {
2023-09-11 12:53:05 +02:00
const newVersionInfo = `New dev version available, commit ${ latestCommitHash . slice ( 0 , 7 ) } . You are using commit ${ commitRef . slice ( 0 , 7 ) } . Refresh the page to update.` ;
setNewInfoMessage ( newVersionInfo );
2023-09-10 15:21:03 +02:00
}
2023-09-10 12:20:31 +02:00
}
} catch ( error ) {
console . error ( 'Failed to fetch latest version info:' , error );
}
};
2023-09-10 15:21:03 +02:00
2023-09-10 12:20:31 +02:00
fetchVersionInfo ();
2023-09-10 16:13:33 +02:00
}, [ setNewInfoMessage , isElectron ]);
2023-09-10 12:20:31 +02:00
2023-03-13 14:29:34 +01:00
2023-03-22 16:23:32 +01:00
// preload banners
2023-03-05 14:26:40 +01:00
useEffect (() => {
2023-03-22 16:23:32 +01:00
const loadImages = async () => {
2023-04-19 14:12:06 +02:00
const images = await importAll ( require . context ( '../public/assets' , true , /\.(png|jpe?g|svg)$/ ));
2023-03-22 16:23:32 +01:00
const imageUrls = images . map (( image ) => image . default );
preloadImages ( imageUrls );
};
loadImages ();
2023-03-05 14:26:40 +01:00
}, []);
2023-03-11 17:27:46 +01:00
2023-03-20 17:08:05 +01:00
// automatic dark mode without interefering with user's selected style
useEffect (() => {
2023-03-21 11:17:54 +01:00
if ( isHomeRoute ) return ;
2023-03-20 17:08:05 +01:00
const darkModeMediaQuery = window . matchMedia ( '(prefers-color-scheme: dark)' );
const isDarkMode = darkModeMediaQuery . matches ;
2023-03-21 11:17:54 +01:00
if ( isDarkMode && ! isHomeRoute ) {
2023-03-20 17:08:05 +01:00
setSelectedStyle ( 'Tomorrow' );
setBodyStyle ({
background : '#1d1f21 none' ,
color : '#c5c8c6' ,
fontFamily : 'Arial, Helvetica, sans-serif'
});
localStorage . setItem ( 'selectedStyle' , 'Tomorrow' );
}
const darkModeListener = ( e ) => {
2023-03-21 11:17:54 +01:00
if ( e . matches && ! isHomeRoute ) {
2023-03-20 17:08:05 +01:00
setSelectedStyle ( 'Tomorrow' );
setBodyStyle ({
background : '#1d1f21 none' ,
color : '#c5c8c6' ,
fontFamily : 'Arial, Helvetica, sans-serif'
});
localStorage . setItem ( 'selectedStyle' , 'Tomorrow' );
}
};
darkModeMediaQuery . addEventListener ( 'change' , darkModeListener );
return () => {
darkModeMediaQuery . removeEventListener ( 'change' , darkModeListener );
};
2023-04-25 21:30:10 +02:00
}, [ isHomeRoute , setBodyStyle , setSelectedStyle ]);
2023-03-21 11:17:54 +01:00
2023-03-20 17:08:05 +01:00
// fetch default subplebbits
useEffect (() => {
let didCancel = false ;
fetch (
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json" ,
{ cache : "no-cache" }
)
. then (( res ) => res . json ())
. then ( res => {
if ( ! didCancel ) {
setDefaultSubplebbits ( res );
}
});
return () => {
didCancel = true ;
};
2023-04-25 21:30:10 +02:00
}, [ setDefaultSubplebbits ]);
2023-03-20 17:08:05 +01:00
// handle nested routes
useEffect (() => {
const path = location . pathname ;
if ( path . endsWith ( '/post' )) {
setShowPostFormLink ( false );
setShowPostForm ( true );
} else {
setShowPostFormLink ( true );
setShowPostForm ( false );
}
if ( path . endsWith ( '/settings' )) {
setIsSettingsOpen ( true );
} else {
setIsSettingsOpen ( false );
}
2023-04-25 21:30:10 +02:00
}, [ location . pathname , setIsSettingsOpen , setShowPostForm , setShowPostFormLink ]);
2023-03-20 17:08:05 +01:00
2023-02-01 13:30:32 +01:00
return (
< div >
< Helmet >
2023-04-29 12:47:30 +02:00
< link rel = "apple-touch-icon" sizes = "180x180" href = "assets/logo/apple-touch-icon.png" />
< link rel = "icon" type = "image/png" sizes = "32x32" href = "assets/logo/favicon-32x32.png" />
< link rel = "icon" type = "image/png" sizes = "16x16" href = "assets/logo/favicon-16x16.png" />
< link rel = "manifest" href = "site.webmanifest" />
2023-02-01 13:30:32 +01:00
< meta name = "msapplication-TileColor" content = "#fee9cd" />
< meta name = "theme-color" content = "#ffffff" />
< /Helmet>
2023-02-08 21:49:36 +01:00
< GlobalStyle
2023-04-09 20:55:55 +02:00
background = { bodyStyle . background }
color = { bodyStyle . color }
fontFamily = { bodyStyle . fontFamily }
2023-02-08 21:49:36 +01:00
/>
2023-02-09 19:08:15 +01:00
< Routes >
2023-02-13 12:05:10 +01:00
< Route exact path = '/' element = { < Home setBodyStyle = { setBodyStyle } /> } />
2023-04-08 22:30:44 +02:00
< Route path = { `/p/:subplebbitAddress` } element = { < Board setBodyStyle = { setBodyStyle } /> } >
2023-02-19 22:08:18 +01:00
< Route path = 'post' element = { < Board /> } />
2023-03-15 15:25:09 +01:00
< Route path = 'settings' element = { < Board /> } />
2023-02-12 21:20:08 +01:00
< /Route>
2023-04-08 22:30:44 +02:00
< Route path = { `/p/:subplebbitAddress/c/:threadCid` } element = { < Thread setBodyStyle = { setBodyStyle } /> } >
2023-02-19 22:08:18 +01:00
< Route path = 'post' element = { < Thread /> } />
2023-03-15 15:25:09 +01:00
< Route path = 'settings' element = { < Thread /> } />
2023-02-11 22:00:13 +01:00
< /Route>
2023-04-08 22:30:44 +02:00
< Route path = { `/p/:subplebbitAddress/catalog` } element = { < Catalog setBodyStyle = { setBodyStyle } /> } >
2023-02-19 22:08:18 +01:00
< Route path = 'post' element = { < Catalog /> } />
2023-03-15 15:25:09 +01:00
< Route path = 'settings' element = { < Catalog /> } />
2023-02-19 16:48:46 +01:00
< /Route>
2023-06-27 21:06:30 +02:00
< Route path = { `/p/:subplebbitAddress/description` } element = { < Description setBodyStyle = { setBodyStyle } /> } >
< Route path = 'settings' element = { < Description /> } />
< /Route>
< Route path = { `/p/:subplebbitAddress/rules` } element = { < Rules setBodyStyle = { setBodyStyle } /> } >
< Route path = 'settings' element = { < Rules /> } />
< /Route>
2023-04-23 08:43:53 +02:00
< Route path = { `/profile/c/:index` } element = { < Pending setBodyStyle = { setBodyStyle } /> } >
< Route path = 'settings' element = { < Pending /> } />
< /Route>
2023-05-07 17:22:55 +02:00
< Route path = { `/p/subscriptions` } element = { < Subscriptions setBodyStyle = { setBodyStyle } /> } >
2023-05-03 11:01:41 +02:00
< Route path = 'settings' element = { < Subscriptions /> } />
< /Route>
2023-05-07 17:48:38 +02:00
< Route path = { `p/subscriptions/catalog` } element = { < SubscriptionsCatalog setBodyStyle = { setBodyStyle } /> } >
2023-05-07 17:22:55 +02:00
< Route path = 'settings' element = { < SubscriptionsCatalog /> } />
2023-05-07 17:48:38 +02:00
< /Route>
2023-05-16 16:37:50 +02:00
< Route path = { `p/all` } element = { < All setBodyStyle = { setBodyStyle } /> } >
< Route path = 'settings' element = { < All /> } />
< /Route>
< Route path = { `p/all/catalog` } element = { < AllCatalog setBodyStyle = { setBodyStyle } /> } >
< Route path = 'settings' element = { < AllCatalog /> } />
< /Route>
2023-03-17 17:49:30 +01:00
< Route path = '*' element = { < NotFound setBodyStyle = { setBodyStyle } /> } />
2023-02-09 19:08:15 +01:00
< /Routes>
2023-04-29 18:01:19 +02:00
< Toast />
2023-04-09 20:55:55 +02:00
< CaptchaModal
selectedStyle = { selectedStyle }
isOpen = { isCaptchaOpen }
closeModal = {() => setIsCaptchaOpen ( false )}
/>
2023-02-01 13:30:32 +01:00
< /div>
2023-03-11 17:27:46 +01:00
)}