add reload badge on sw update

This commit is contained in:
Bekbolsun 2024-02-19 21:41:10 +06:00
parent adbc7d455d
commit 59c03d16eb
8 changed files with 116 additions and 32 deletions

View File

@ -9,16 +9,17 @@ import { ModalInitial } from './components/Modal/ModalInitial/ModalInitial'
import { ModalImportKeys } from './components/Modal/ModalImportKeys/ModalImportKeys' import { ModalImportKeys } from './components/Modal/ModalImportKeys/ModalImportKeys'
import { ModalSignUp } from './components/Modal/ModalSignUp/ModalSignUp' import { ModalSignUp } from './components/Modal/ModalSignUp/ModalSignUp'
import { ModalLogin } from './components/Modal/ModalLogin/ModalLogin' import { ModalLogin } from './components/Modal/ModalLogin/ModalLogin'
import { useSearchParams } from 'react-router-dom'
function App() { function App() {
const [render, setRender] = useState(0) const [render, setRender] = useState(0)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const [searchParams, setSearchParams] = useSearchParams()
const [isConnected, setIsConnected] = useState(false) const [isConnected, setIsConnected] = useState(false)
const load = useCallback(async () => { const load = useCallback(async () => {
const keys: DbKey[] = await dbi.listKeys() const keys: DbKey[] = await dbi.listKeys()
// console.log(keys, 'keys')
dispatch(setKeys({ keys })) dispatch(setKeys({ keys }))
const loadProfiles = async () => { const loadProfiles = async () => {
@ -55,7 +56,6 @@ function App() {
// rerender // rerender
// setRender((r) => r + 1) // setRender((r) => r + 1)
// if (!keys.length) handleOpen(MODAL_PARAMS_KEYS.INITIAL)
// eslint-disable-next-line // eslint-disable-next-line
}, [dispatch]) }, [dispatch])
@ -80,12 +80,14 @@ function App() {
// subscribe to service worker updates // subscribe to service worker updates
swicOnReload(() => { swicOnReload(() => {
console.log('reload') console.log('reload')
// FIXME show 'Please reload' badge at page top searchParams.set('reload', 'true')
setSearchParams(searchParams)
}) })
return ( return (
<> <>
<AppRoutes /> <AppRoutes />
<ModalInitial /> <ModalInitial />
<ModalImportKeys /> <ModalImportKeys />
<ModalSignUp /> <ModalSignUp />

View File

@ -13,7 +13,7 @@ import { DOMAIN } from '@/utils/consts'
import { fetchNip05 } from '@/utils/helpers/helpers' import { fetchNip05 } from '@/utils/helpers/helpers'
import { Stack, Typography, useTheme } from '@mui/material' import { Stack, Typography, useTheme } from '@mui/material'
import { ChangeEvent, Fragment, useCallback, useEffect, useState } from 'react' import { ChangeEvent, Fragment, useCallback, useEffect, useState } from 'react'
import { useParams, useSearchParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { StyledSettingContainer } from './styled' import { StyledSettingContainer } from './styled'
import { SectionTitle } from '@/shared/SectionTitle/SectionTitle' import { SectionTitle } from '@/shared/SectionTitle/SectionTitle'
@ -55,7 +55,7 @@ export const ModalEditName = () => {
setIsAvailable(true) setIsAvailable(true)
setIsChecking(false) setIsChecking(false)
} }
}, [debouncedName]) }, [debouncedName, npub])
useEffect(() => { useEffect(() => {
checkIsUsernameAvailable() checkIsUsernameAvailable()

View File

@ -0,0 +1,33 @@
import { FC } from 'react'
import { Collapse, Stack, Typography } from '@mui/material'
import { useSearchParams } from 'react-router-dom'
import { StyledAlert, StyledReloadButton } from './styled'
const ReloadBadgeContent: FC = () => {
const [searchParams, setSearchParams] = useSearchParams()
const handleReload = () => {
searchParams.delete('reload')
setSearchParams(searchParams)
window.location.reload()
}
return (
<Collapse in>
<StyledAlert>
<Stack direction={'row'} className="content">
<Typography flex={1} className="title">
New version available, please reload the page!
</Typography>
<StyledReloadButton onClick={handleReload}>Reload</StyledReloadButton>
</Stack>
</StyledAlert>
</Collapse>
)
}
export const ReloadBadge = () => {
const [searchParams] = useSearchParams()
const open = searchParams.get('reload') === 'true'
return <>{open && <ReloadBadgeContent />}</>
}

View File

@ -0,0 +1,50 @@
import { AppButtonProps, Button } from '@/shared/Button/Button'
import { Alert, AlertProps, styled } from '@mui/material'
import RefreshIcon from '@mui/icons-material/Refresh'
export const StyledAlert = styled((props: AlertProps) => (
<Alert {...props} variant="outlined" severity="info" classes={{ message: 'message' }} />
))(() => {
return {
height: 'auto',
marginTop: '0.5rem',
alignItems: 'center',
'& .message': {
flex: 1,
overflow: 'initial',
},
'& .content': {
width: '100%',
alignItems: 'center',
gap: '1rem',
},
'& .title': {
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
'@media screen and (max-width: 320px)': {
'& .title': {
fontSize: '14px',
WebkitLineClamp: 2,
},
},
}
})
export const StyledReloadButton = styled((props: AppButtonProps) => <Button startIcon={<RefreshIcon />} {...props} />)(
({ theme }) => {
const isDarkTheme = theme.palette.mode === 'dark'
return {
padding: '0.25rem 1rem',
'&.button:is(:hover, :active, &)': {
background: isDarkTheme ? '#b8e7fb' : '#014361',
},
'@media screen and (max-width: 320px)': {
padding: '0.25rem 0.5rem',
},
}
}
)

View File

@ -1,7 +1,7 @@
import { Avatar, Stack, Toolbar, Typography } from '@mui/material' import { Avatar, Stack, Toolbar, Typography, Divider, DividerProps, styled } from '@mui/material'
import { StyledAppBar, StyledAppLogo, StyledAppName, StyledProfileContainer, StyledThemeButton } from './styled' import { StyledAppBar, StyledAppLogo, StyledAppName, StyledProfileContainer, StyledThemeButton } from './styled'
import { Menu } from './components/Menu' import { Menu } from './components/Menu'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { ProfileMenu } from './components/ProfileMenu' import { ProfileMenu } from './components/ProfileMenu'
import { useProfile } from '@/hooks/useProfile' import { useProfile } from '@/hooks/useProfile'
import DarkModeIcon from '@mui/icons-material/DarkMode' import DarkModeIcon from '@mui/icons-material/DarkMode'
@ -13,6 +13,8 @@ export const Header = () => {
const themeMode = useAppSelector((state) => state.ui.themeMode) const themeMode = useAppSelector((state) => state.ui.themeMode)
const navigate = useNavigate() const navigate = useNavigate()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const [searchParams] = useSearchParams()
const needReload = searchParams.get('reload') === 'true'
const { npub = '' } = useParams<{ npub: string }>() const { npub = '' } = useParams<{ npub: string }>()
const { userName, userAvatar, avatarTitle } = useProfile(npub) const { userName, userAvatar, avatarTitle } = useProfile(npub)
@ -30,7 +32,7 @@ export const Header = () => {
} }
return ( return (
<StyledAppBar position="fixed"> <StyledAppBar position={needReload ? 'relative' : 'fixed'}>
<Toolbar sx={{ padding: '12px' }}> <Toolbar sx={{ padding: '12px' }}>
<Stack direction={'row'} justifyContent={'space-between'} alignItems={'center'} width={'100%'}> <Stack direction={'row'} justifyContent={'space-between'} alignItems={'center'} width={'100%'}>
{showProfile && ( {showProfile && (
@ -56,6 +58,15 @@ export const Header = () => {
{showProfile ? <ProfileMenu /> : <Menu />} {showProfile ? <ProfileMenu /> : <Menu />}
</Stack> </Stack>
</Toolbar> </Toolbar>
<StyledDivider />
</StyledAppBar> </StyledAppBar>
) )
} }
const StyledDivider = styled((props: DividerProps) => <Divider {...props} />)({
position: 'absolute',
bottom: 0,
width: '100%',
left: 0,
height: '2px',
})

View File

@ -1,13 +1,17 @@
import { FC } from 'react' import { FC } from 'react'
import { Outlet } from 'react-router-dom' import { Outlet, useSearchParams } from 'react-router-dom'
import { Header } from './Header/Header' import { Header } from './Header/Header'
import { Container, ContainerProps, Divider, DividerProps, styled } from '@mui/material' import { Container, ContainerProps, styled } from '@mui/material'
import { ReloadBadge } from '@/components/ReloadBadge/ReloadBadge'
export const Layout: FC = () => { export const Layout: FC = () => {
const [searchParams] = useSearchParams()
const needReload = searchParams.get('reload') === 'true'
return ( return (
<StyledContainer maxWidth="md"> <StyledContainer maxWidth="md" className={needReload ? 'reload' : ''}>
<ReloadBadge />
<Header /> <Header />
<StyledDivider />
<main> <main>
<Outlet /> <Outlet />
</main> </main>
@ -21,17 +25,11 @@ const StyledContainer = styled((props: ContainerProps) => <Container maxWidth="s
flexDirection: 'column', flexDirection: 'column',
paddingBottom: '1rem', paddingBottom: '1rem',
position: 'relative', position: 'relative',
'& > main': { '&': {
flex: 1, flex: 1,
maxHeight: '100%', maxHeight: '100%',
},
'&:not(.reload) > main': {
paddingTop: 'calc(66px + 1rem)', paddingTop: 'calc(66px + 1rem)',
}, },
}) })
const StyledDivider = styled((props: DividerProps) => <Divider {...props} />)({
position: 'absolute',
top: '66px',
width: '100%',
left: 0,
height: '2px',
})

View File

@ -21,7 +21,6 @@ const AppRoutes = () => {
<Routes> <Routes>
<Route path="/" element={<Layout />}> <Route path="/" element={<Layout />}>
<Route path="/" element={<Navigate to={'/home'} />} /> <Route path="/" element={<Navigate to={'/home'} />} />
{/* <Route path='/welcome' element={<WelcomePage />} /> */}
<Route path="/home" element={<HomePage />} /> <Route path="/home" element={<HomePage />} />
<Route path="/key/:npub" element={<KeyPage />} /> <Route path="/key/:npub" element={<KeyPage />} />
<Route path="/key/:npub/app/:appNpub" element={<AppPage />} /> <Route path="/key/:npub/app/:appNpub" element={<AppPage />} />

View File

@ -2,16 +2,7 @@ import { combineReducers, configureStore } from '@reduxjs/toolkit'
import { contentSlice } from './reducers/content.slice' import { contentSlice } from './reducers/content.slice'
import { uiSlice } from './reducers/ui.slice' import { uiSlice } from './reducers/ui.slice'
import { import { persistStore, persistReducer } from 'redux-persist'
persistStore,
persistReducer,
// FLUSH,
// REGISTER,
// REHYDRATE,
// PAUSE,
// PERSIST,
// PURGE,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import memoizeOne from 'memoize-one' import memoizeOne from 'memoize-one'
import isDeepEqual from 'lodash.isequal' import isDeepEqual from 'lodash.isequal'