github.com/grafviktor/keep-my-secret@v0.9.10-0.20230908165355-19f35cce90e5/website/src/components/App/index.jsx (about) 1 import get from 'lodash/get' 2 import {useMemo, useState, useEffect} from 'react' 3 import Navigation from '../Navigation' 4 import Error from '../Error' 5 import Login from '../Login' 6 import Register from '../Register' 7 import Home from '../Home' 8 import PaymentCard from '../Home/PaymentCard' 9 import File from '../Home/File' 10 import Note from '../Home/Note' 11 import Password from '../Home/Password' 12 import ApplicationContext from '../../context' 13 import {isTokenExpired} from '../../utils' 14 import createAPI from '../../api' 15 16 import './style.css' 17 18 const viewToComponentMap = { 19 login : Login, 20 register : Register, 21 home : Home, 22 card : PaymentCard, 23 file : File, 24 note : Note, 25 pass : Password, 26 } 27 28 export default () => { 29 const [alertMessage, setAlertMessage] = useState('') 30 const [accessToken, setAccessToken] = useState('') 31 const isLoggedIn = () => !isTokenExpired(accessToken) 32 const [view, navigateTo] = useState('login') 33 const [refreshTokenIngervalID, setRefreshTokenIntervalID] = useState(null) 34 const [loggedIn, setLoggedIn] = useState(false) 35 const [secret, setSecret] = useState(null) 36 const [secrets, setSecrets] = useState({}) 37 const [appBusy, setAppBusy] = useState(false) 38 39 const api = createAPI({setAppBusy, navigateTo}) 40 41 const getVersion = () => api.getVersion() 42 43 // eslint-disable-next-line no-shadow 44 const createSecret = async (secret) => api.createSecret(accessToken, secret) 45 46 // eslint-disable-next-line no-shadow 47 const updateSecret = (secret, id) => api.updateSecret(accessToken, id, secret) 48 49 const deleteSecret = (id) => api.deleteSecret(accessToken, id) 50 51 const getSecretFile = async (id) => { 52 const response = await api.getSecretFile(accessToken, id) 53 const url = window.URL.createObjectURL(new Blob([response.data])) 54 const link = document.createElement('a') 55 const contentDispositionHeader = get(response, 'headers.content-disposition', '') 56 const match = contentDispositionHeader.match(/.*filename=([^"]+)/) 57 58 if (match) { 59 link.href = url 60 link.setAttribute('download', match[1]) 61 document.body.appendChild(link) 62 link.click() 63 } 64 } 65 66 const fetchSecrets = () => api.fetchSecrets(accessToken) 67 68 useEffect(() => { 69 if (!loggedIn) { 70 navigateTo('login') 71 } 72 }, [loggedIn]) 73 74 useEffect(() => { 75 if (isTokenExpired(accessToken)) { 76 setLoggedIn(false) 77 } else { 78 setLoggedIn(true) 79 } 80 }, [accessToken]) 81 82 useEffect(() => { 83 if (!isTokenExpired(accessToken)) { 84 setRefreshTokenIntervalID(setInterval(async () => { 85 try { 86 await api.refreshToken() 87 } catch (error) { 88 console.warn('Warning: unable to get refreshed token') 89 } 90 }, 1000 * 60)) 91 } else { 92 clearInterval(refreshTokenIngervalID) 93 } 94 }, [accessToken]) 95 96 const contextValue = useMemo(() => ({ 97 // verbs 98 setSecret, 99 getVersion, 100 isLoggedIn, 101 navigateTo, 102 setSecrets, 103 createSecret, 104 updateSecret, 105 fetchSecrets, 106 deleteSecret, 107 getSecretFile, 108 setAccessToken, 109 setAlertMessage, 110 111 // and nouns 112 api, 113 secret, 114 secrets, 115 loggedIn, 116 })) 117 118 const ActiveComponent = viewToComponentMap[view] || Login 119 120 return ( 121 <div className="application"> 122 <ApplicationContext.Provider value={contextValue}> 123 <Navigation loggedIn={loggedIn} /> 124 <Error message={alertMessage} view={view} /> 125 <ActiveComponent loggedIn={loggedIn} appBusy={appBusy} /> 126 </ApplicationContext.Provider> 127 </div> 128 ) 129 }