fix: show new version info toast only once

This commit is contained in:
plebeius.eth
2023-09-18 11:55:30 +02:00
parent d685498b64
commit 3f33dcb37a
3 changed files with 202 additions and 210 deletions
+6 -6
View File
@@ -128,26 +128,26 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@capacitor/cli": "3.6.0", "@capacitor/cli": "3.6.0",
"concurrently": "8.0.1", "concurrently": "8.0.1",
"conventional-changelog-cli": "4.1.0",
"cordova-res": "0.15.4", "cordova-res": "0.15.4",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cz-conventional-changelog": "^3.3.0",
"decompress": "4.2.1", "decompress": "4.2.1",
"electron": "19.1.8", "electron": "19.1.8",
"electron-builder": "23.0.9", "electron-builder": "23.0.9",
"husky": "4.3.8",
"lint-staged": "12.3.8",
"prettier": "2.3.2", "prettier": "2.3.2",
"source-map-explorer": "2.5.3", "source-map-explorer": "2.5.3",
"typescript": "5.0.2", "typescript": "5.0.2",
"wait-on": "7.0.1", "wait-on": "7.0.1"
"conventional-changelog-cli": "4.1.0",
"cz-conventional-changelog": "3.3.0",
"husky": "4.3.8",
"lint-staged": "12.3.8"
}, },
"resolutions": { "resolutions": {
"styled-components": "^5" "styled-components": "^5"
}, },
"lint-staged": { "lint-staged": {
"{src,test,config}/**/*.{cjs,js,jsx,ts,tsx}": [ "{src,test,config}/**/*.{cjs,js,jsx,ts,tsx}": [
"prettier --config config/prettier.config.js --write" "prettier --config prettier.config.js --write"
] ]
}, },
"husky": { "husky": {
+188 -196
View File
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { Route, Routes, useLocation } from 'react-router-dom'; import { Route, Routes, useLocation } from 'react-router-dom';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import 'react-tooltip/dist/react-tooltip.css'; import 'react-tooltip/dist/react-tooltip.css';
@@ -21,224 +21,216 @@ import Thread from './components/views/Thread';
import CaptchaModal from './components/modals/CaptchaModal'; import CaptchaModal from './components/modals/CaptchaModal';
import { importAll } from './components/ImageBanner'; import { importAll } from './components/ImageBanner';
import preloadImages from './utils/preloadImages'; import preloadImages from './utils/preloadImages';
import useError from "./hooks/useError"; import useError from './hooks/useError';
import useInfo from "./hooks/useInfo"; import useInfo from './hooks/useInfo';
import useSuccess from "./hooks/useSuccess"; import useSuccess from './hooks/useSuccess';
import packageJson from '../package.json'; import packageJson from '../package.json';
const commitRef = process?.env?.REACT_APP_COMMIT_REF || ''; const commitRef = process?.env?.REACT_APP_COMMIT_REF || '';
export default function App() { export default function App() {
const { const {
bodyStyle, setBodyStyle, bodyStyle,
setDefaultSubplebbits, setBodyStyle,
isCaptchaOpen, setIsCaptchaOpen, setDefaultSubplebbits,
setIsSettingsOpen, isCaptchaOpen,
selectedStyle, setSelectedStyle, setIsCaptchaOpen,
setShowPostForm, setIsSettingsOpen,
setShowPostFormLink, selectedStyle,
} = useGeneralStore(state => state); setSelectedStyle,
setShowPostForm,
setShowPostFormLink,
} = useGeneralStore((state) => state);
const location = useLocation(); const location = useLocation();
const isHomeRoute = location.pathname === "/"; const isHomeRoute = location.pathname === '/';
const isElectron = window.electron && window.electron.isElectron; const isElectron = window.electron && window.electron.isElectron;
const [, setNewErrorMessage] = useError(); const [, setNewErrorMessage] = useError();
const [, setNewSuccessMessage] = useSuccess(); const [, setNewSuccessMessage] = useSuccess();
const [, setNewInfoMessage] = useInfo(); const [, setNewInfoMessage] = useInfo();
const [infoMessageShown, setInfoMessageShown] = useState(false);
useEffect(() => { useEffect(() => {
const successToast = localStorage.getItem("successToast"); const successToast = localStorage.getItem('successToast');
const errorToast = localStorage.getItem("errorToast"); const errorToast = localStorage.getItem('errorToast');
if (successToast) { if (successToast) {
setNewSuccessMessage(successToast); setNewSuccessMessage(successToast);
localStorage.removeItem("successToast"); localStorage.removeItem('successToast');
} else if (errorToast) { } else if (errorToast) {
setNewErrorMessage(errorToast); setNewErrorMessage(errorToast);
localStorage.removeItem("errorToast"); localStorage.removeItem('errorToast');
} else { } else {
return; return;
} }
}, [setNewErrorMessage, setNewSuccessMessage]); }, [setNewErrorMessage, setNewSuccessMessage]);
// check for new version
// TODO: delete this code and toast when v1.0.0 is released
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();
// check for new version if (packageJson.version !== packageData.version && !infoMessageShown) {
// TODO: delete this code and toast when v1.0.0 is released const newVersionInfo = isElectron
useEffect(() => { ? `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Download the latest version here: https://github.com/plebbit/plebchan/releases/latest`
const fetchVersionInfo = async () => { : `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Refresh to update.`;
try { setNewInfoMessage(newVersionInfo);
const packageRes = await fetch('https://raw.githubusercontent.com/plebbit/plebchan/master/package.json', { cache: 'no-cache' }); setInfoMessageShown(true);
const packageData = await packageRes.json(); }
if (packageJson.version !== packageData.version) { if (commitRef.length > 0) {
const newVersionInfo = isElectron const commitRes = await fetch('https://api.github.com/repos/plebbit/plebchan/commits?per_page=1&sha=development', { cache: 'no-cache' });
? `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Download the latest version here: https://github.com/plebbit/plebchan/releases/latest` const commitData = await commitRes.json();
: `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Refresh to update.`;
setNewInfoMessage(newVersionInfo);
}
if (commitRef.length > 0) { const latestCommitHash = commitData[0].sha;
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();
const latestCommitHash = commitData[0].sha; if (latestCommitHash.trim() !== commitRef.trim() && !infoMessageShown) {
const newVersionInfo = `New dev version available, commit ${latestCommitHash.slice(0, 7)}. You are using commit ${commitRef.slice(0, 7)}. Refresh to update.`;
setNewInfoMessage(newVersionInfo);
setInfoMessageShown(true);
}
}
} catch (error) {
console.error('Failed to fetch latest version info:', error);
}
};
if (latestCommitHash.trim() !== commitRef.trim()) { fetchVersionInfo();
const newVersionInfo = `New dev version available, commit ${latestCommitHash.slice(0, 7)}. You are using commit ${commitRef.slice(0, 7)}. Refresh to update.`; }, [setNewInfoMessage, isElectron, infoMessageShown]);
setNewInfoMessage(newVersionInfo);
}
}
} catch (error) {
console.error('Failed to fetch latest version info:', error);
}
};
fetchVersionInfo(); // preload banners
}, [setNewInfoMessage, isElectron]); useEffect(() => {
const loadImages = async () => {
const images = await importAll(require.context('../public/assets', true, /\.(png|jpe?g|svg)$/));
const imageUrls = images.map((image) => image.default);
preloadImages(imageUrls);
};
loadImages();
}, []);
// preload banners // automatic dark mode without interefering with user's selected style
useEffect(() => { useEffect(() => {
const loadImages = async () => { if (isHomeRoute) return;
const images = await importAll(require.context('../public/assets', true, /\.(png|jpe?g|svg)$/));
const imageUrls = images.map((image) => image.default);
preloadImages(imageUrls);
};
loadImages(); const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
}, []); const isDarkMode = darkModeMediaQuery.matches;
if (isDarkMode && !isHomeRoute) {
setSelectedStyle('Tomorrow');
setBodyStyle({
background: '#1d1f21 none',
color: '#c5c8c6',
fontFamily: 'Arial, Helvetica, sans-serif',
});
localStorage.setItem('selectedStyle', 'Tomorrow');
}
// automatic dark mode without interefering with user's selected style const darkModeListener = (e) => {
useEffect(() => { if (e.matches && !isHomeRoute) {
if (isHomeRoute) return; setSelectedStyle('Tomorrow');
setBodyStyle({
background: '#1d1f21 none',
color: '#c5c8c6',
fontFamily: 'Arial, Helvetica, sans-serif',
});
localStorage.setItem('selectedStyle', 'Tomorrow');
}
};
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); darkModeMediaQuery.addEventListener('change', darkModeListener);
const isDarkMode = darkModeMediaQuery.matches;
if (isDarkMode && !isHomeRoute) { return () => {
setSelectedStyle('Tomorrow'); darkModeMediaQuery.removeEventListener('change', darkModeListener);
setBodyStyle({ };
background: '#1d1f21 none', }, [isHomeRoute, setBodyStyle, setSelectedStyle]);
color: '#c5c8c6',
fontFamily: 'Arial, Helvetica, sans-serif'
});
localStorage.setItem('selectedStyle', 'Tomorrow');
}
const darkModeListener = (e) => { // fetch default subplebbits
if (e.matches && !isHomeRoute) { useEffect(() => {
setSelectedStyle('Tomorrow'); let didCancel = false;
setBodyStyle({ fetch('https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json', { cache: 'no-cache' })
background: '#1d1f21 none', .then((res) => res.json())
color: '#c5c8c6', .then((res) => {
fontFamily: 'Arial, Helvetica, sans-serif' if (!didCancel) {
}); setDefaultSubplebbits(res);
localStorage.setItem('selectedStyle', 'Tomorrow'); }
} });
}; return () => {
didCancel = true;
};
}, [setDefaultSubplebbits]);
darkModeMediaQuery.addEventListener('change', darkModeListener); // handle nested routes
useEffect(() => {
const path = location.pathname;
if (path.endsWith('/post')) {
setShowPostFormLink(false);
setShowPostForm(true);
} else {
setShowPostFormLink(true);
setShowPostForm(false);
}
return () => { if (path.endsWith('/settings')) {
darkModeMediaQuery.removeEventListener('change', darkModeListener); setIsSettingsOpen(true);
}; } else {
}, [isHomeRoute, setBodyStyle, setSelectedStyle]); setIsSettingsOpen(false);
}
}, [location.pathname, setIsSettingsOpen, setShowPostForm, setShowPostFormLink]);
// fetch default subplebbits return (
useEffect(() => { <div>
let didCancel = false; <Helmet>
fetch( <link rel='apple-touch-icon' sizes='180x180' href='assets/logo/apple-touch-icon.png' />
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json", <link rel='icon' type='image/png' sizes='32x32' href='assets/logo/favicon-32x32.png' />
{ cache: "no-cache" } <link rel='icon' type='image/png' sizes='16x16' href='assets/logo/favicon-16x16.png' />
) <link rel='manifest' href='site.webmanifest' />
.then((res) => res.json()) <meta name='msapplication-TileColor' content='#fee9cd' />
.then(res => { <meta name='theme-color' content='#ffffff' />
if (!didCancel) { </Helmet>
setDefaultSubplebbits(res); <GlobalStyle background={bodyStyle.background} color={bodyStyle.color} fontFamily={bodyStyle.fontFamily} />
} <Routes>
}); <Route exact path='/' element={<Home setBodyStyle={setBodyStyle} />} />
return () => { <Route path={`/p/:subplebbitAddress`} element={<Board setBodyStyle={setBodyStyle} />}>
didCancel = true; <Route path='post' element={<Board />} />
}; <Route path='settings' element={<Board />} />
}, [setDefaultSubplebbits]); </Route>
<Route path={`/p/:subplebbitAddress/c/:threadCid`} element={<Thread setBodyStyle={setBodyStyle} />}>
// handle nested routes <Route path='post' element={<Thread />} />
useEffect(() => { <Route path='settings' element={<Thread />} />
const path = location.pathname; </Route>
if (path.endsWith('/post')) { <Route path={`/p/:subplebbitAddress/catalog`} element={<Catalog setBodyStyle={setBodyStyle} />}>
setShowPostFormLink(false); <Route path='post' element={<Catalog />} />
setShowPostForm(true); <Route path='settings' element={<Catalog />} />
} else { </Route>
setShowPostFormLink(true); <Route path={`/p/:subplebbitAddress/description`} element={<Description setBodyStyle={setBodyStyle} />}>
setShowPostForm(false); <Route path='settings' element={<Description />} />
} </Route>
<Route path={`/p/:subplebbitAddress/rules`} element={<Rules setBodyStyle={setBodyStyle} />}>
if (path.endsWith('/settings')) { <Route path='settings' element={<Rules />} />
setIsSettingsOpen(true); </Route>
} else { <Route path={`/profile/c/:index`} element={<Pending setBodyStyle={setBodyStyle} />}>
setIsSettingsOpen(false); <Route path='settings' element={<Pending />} />
} </Route>
<Route path={`/p/subscriptions`} element={<Subscriptions setBodyStyle={setBodyStyle} />}>
}, [location.pathname, setIsSettingsOpen, setShowPostForm, setShowPostFormLink]); <Route path='settings' element={<Subscriptions />} />
</Route>
return ( <Route path={`p/subscriptions/catalog`} element={<SubscriptionsCatalog setBodyStyle={setBodyStyle} />}>
<div> <Route path='settings' element={<SubscriptionsCatalog />} />
<Helmet> </Route>
<link rel="apple-touch-icon" sizes="180x180" href="assets/logo/apple-touch-icon.png" /> <Route path={`p/all`} element={<All setBodyStyle={setBodyStyle} />}>
<link rel="icon" type="image/png" sizes="32x32" href="assets/logo/favicon-32x32.png" /> <Route path='settings' element={<All />} />
<link rel="icon" type="image/png" sizes="16x16" href="assets/logo/favicon-16x16.png" /> </Route>
<link rel="manifest" href="site.webmanifest" /> <Route path={`p/all/catalog`} element={<AllCatalog setBodyStyle={setBodyStyle} />}>
<meta name="msapplication-TileColor" content="#fee9cd" /> <Route path='settings' element={<AllCatalog />} />
<meta name="theme-color" content="#ffffff" /> </Route>
</Helmet> <Route path='*' element={<NotFound setBodyStyle={setBodyStyle} />} />
<GlobalStyle </Routes>
background={bodyStyle.background} <Toast />
color={bodyStyle.color} <CaptchaModal selectedStyle={selectedStyle} isOpen={isCaptchaOpen} closeModal={() => setIsCaptchaOpen(false)} />
fontFamily={bodyStyle.fontFamily} </div>
/> );
<Routes> }
<Route exact path='/' element={<Home setBodyStyle={setBodyStyle} />} />
<Route path={`/p/:subplebbitAddress`} element={<Board setBodyStyle={setBodyStyle} />}>
<Route path='post' element={<Board />} />
<Route path='settings' element={<Board />} />
</Route>
<Route path={`/p/:subplebbitAddress/c/:threadCid`} element={<Thread setBodyStyle={setBodyStyle} />}>
<Route path='post' element={<Thread />} />
<Route path='settings' element={<Thread />} />
</Route>
<Route path={`/p/:subplebbitAddress/catalog`} element={<Catalog setBodyStyle={setBodyStyle} /> }>
<Route path='post' element={<Catalog />} />
<Route path='settings' element={<Catalog />} />
</Route>
<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>
<Route path={`/profile/c/:index`} element={<Pending setBodyStyle={setBodyStyle} /> }>
<Route path='settings' element={<Pending />} />
</Route>
<Route path={`/p/subscriptions`} element={<Subscriptions setBodyStyle={setBodyStyle} /> }>
<Route path='settings' element={<Subscriptions />} />
</Route>
<Route path={`p/subscriptions/catalog`} element={<SubscriptionsCatalog setBodyStyle={setBodyStyle} /> }>
<Route path='settings' element={<SubscriptionsCatalog />} />
</Route>
<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>
<Route path='*' element={<NotFound setBodyStyle={setBodyStyle} />} />
</Routes>
<Toast />
<CaptchaModal
selectedStyle={selectedStyle}
isOpen={isCaptchaOpen}
closeModal={() => setIsCaptchaOpen(false)}
/>
</div>
)}
+1 -1
View File
@@ -6198,7 +6198,7 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
cz-conventional-changelog@3.3.0: cz-conventional-changelog@3.3.0, cz-conventional-changelog@^3.3.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2" resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2"
integrity sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw== integrity sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==