parent
efe3c2c9c7
commit
37baf57093
20
src/App.tsx
20
src/App.tsx
@ -4,8 +4,6 @@ import { Navigate, Route, Routes } from 'react-router-dom'
|
|||||||
import { useAppSelector, useAuth } from './hooks'
|
import { useAppSelector, useAuth } from './hooks'
|
||||||
|
|
||||||
import { MainLayout } from './layouts/Main'
|
import { MainLayout } from './layouts/Main'
|
||||||
|
|
||||||
import { appPrivateRoutes, appPublicRoutes } from './routes'
|
|
||||||
import {
|
import {
|
||||||
privateRoutes,
|
privateRoutes,
|
||||||
publicRoutes,
|
publicRoutes,
|
||||||
@ -16,7 +14,7 @@ import './App.scss'
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const { checkSession } = useAuth()
|
const { checkSession } = useAuth()
|
||||||
const authState = useAppSelector((state) => state.auth)
|
const isLoggedIn = useAppSelector((state) => state.auth?.loggedIn)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.location.hostname === '0.0.0.0') {
|
if (window.location.hostname === '0.0.0.0') {
|
||||||
@ -29,19 +27,9 @@ const App = () => {
|
|||||||
checkSession()
|
checkSession()
|
||||||
}, [checkSession])
|
}, [checkSession])
|
||||||
|
|
||||||
const handleRootRedirect = () => {
|
|
||||||
if (authState.loggedIn) return appPrivateRoutes.homePage
|
|
||||||
|
|
||||||
const callbackPathEncoded = btoa(
|
|
||||||
window.location.href.split(`${window.location.origin}/#`)[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
return `${appPublicRoutes.landingPage}?callbackPath=${callbackPathEncoded}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
||||||
const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => {
|
const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => {
|
||||||
return !authState.loggedIn || !r.hiddenWhenLoggedIn
|
return !isLoggedIn || !r.hiddenWhenLoggedIn
|
||||||
})
|
})
|
||||||
|
|
||||||
const privateRouteList = recursiveRouteRenderer(privateRoutes)
|
const privateRouteList = recursiveRouteRenderer(privateRoutes)
|
||||||
@ -49,9 +37,9 @@ const App = () => {
|
|||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<MainLayout />}>
|
<Route element={<MainLayout />}>
|
||||||
{authState?.loggedIn && privateRouteList}
|
|
||||||
{publicRoutesList}
|
{publicRoutesList}
|
||||||
<Route path="*" element={<Navigate to={handleRootRedirect()} />} />
|
{privateRouteList}
|
||||||
|
<Route path="*" element={<Navigate to={'/'} />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
|
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
import { getPublicKey, nip19 } from 'nostr-tools'
|
import { getPublicKey, nip19 } from 'nostr-tools'
|
||||||
|
|
||||||
import { init as initNostrLogin } from 'nostr-login'
|
import { init as initNostrLogin } from 'nostr-login'
|
||||||
import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
|
import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
|
||||||
|
|
||||||
import { AppBar } from '../components/AppBar/AppBar'
|
import { AppBar } from '../components/AppBar/AppBar'
|
||||||
import { LoadingSpinner } from '../components/LoadingSpinner'
|
import { LoadingSpinner } from '../components/LoadingSpinner'
|
||||||
|
|
||||||
import { NostrController } from '../controllers'
|
import { NostrController } from '../controllers'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useAppDispatch,
|
useAppDispatch,
|
||||||
useAppSelector,
|
useAppSelector,
|
||||||
@ -19,7 +14,6 @@ import {
|
|||||||
useNDK,
|
useNDK,
|
||||||
useNDKContext
|
useNDKContext
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
restoreState,
|
restoreState,
|
||||||
setUserProfile,
|
setUserProfile,
|
||||||
@ -30,9 +24,7 @@ import {
|
|||||||
setUserRobotImage
|
setUserRobotImage
|
||||||
} from '../store/actions'
|
} from '../store/actions'
|
||||||
import { LoginMethod } from '../store/auth/types'
|
import { LoginMethod } from '../store/auth/types'
|
||||||
|
|
||||||
import { getRoboHashPicture, loadState } from '../utils'
|
import { getRoboHashPicture, loadState } from '../utils'
|
||||||
|
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
|
|
||||||
export const MainLayout = () => {
|
export const MainLayout = () => {
|
||||||
@ -53,29 +45,32 @@ export const MainLayout = () => {
|
|||||||
// Ref to track if `subscribeForSigits` has been called
|
// Ref to track if `subscribeForSigits` has been called
|
||||||
const hasSubscribed = useRef(false)
|
const hasSubscribed = useRef(false)
|
||||||
|
|
||||||
const navigateAfterLogin = (path: string) => {
|
const navigateAfterLogin = useCallback(
|
||||||
const callbackPath = searchParams.get('callbackPath')
|
(path: string) => {
|
||||||
|
const isCallback = window.location.hash.startsWith('#/?callbackPath=')
|
||||||
if (callbackPath) {
|
if (isCallback) {
|
||||||
// base64 decoded path
|
const path = atob(window.location.hash.replace('#/?callbackPath=', ''))
|
||||||
const path = atob(callbackPath)
|
setSearchParams((prev) => {
|
||||||
|
prev.delete('callbackPath')
|
||||||
|
return prev
|
||||||
|
})
|
||||||
|
navigate(path)
|
||||||
|
return
|
||||||
|
}
|
||||||
navigate(path)
|
navigate(path)
|
||||||
return
|
},
|
||||||
}
|
[navigate, setSearchParams]
|
||||||
|
)
|
||||||
navigate(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
const login = useCallback(async () => {
|
const login = useCallback(async () => {
|
||||||
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
try {
|
||||||
|
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
||||||
const nostrController = NostrController.getInstance()
|
const nostrController = NostrController.getInstance()
|
||||||
const pubkey = await nostrController.capturePublicKey()
|
const pubkey = await nostrController.capturePublicKey()
|
||||||
|
const redirectPath = await authAndGetMetadataAndRelaysMap(pubkey)
|
||||||
const redirectPath = await authAndGetMetadataAndRelaysMap(pubkey)
|
|
||||||
|
|
||||||
if (redirectPath) {
|
|
||||||
navigateAfterLogin(redirectPath)
|
navigateAfterLogin(redirectPath)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error occured during login`, error)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { Box, Button } from '@mui/material'
|
import { Box, Button } from '@mui/material'
|
||||||
import { useEffect } from 'react'
|
import { Outlet } from 'react-router-dom'
|
||||||
import { Outlet, useLocation } from 'react-router-dom'
|
|
||||||
import { saveVisitedLink } from '../../utils'
|
|
||||||
import { CardComponent } from '../../components/Landing/CardComponent/CardComponent'
|
import { CardComponent } from '../../components/Landing/CardComponent/CardComponent'
|
||||||
import { Container } from '../../components/Container'
|
import { Container } from '../../components/Container'
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
@ -20,13 +18,19 @@ import {
|
|||||||
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
|
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
|
||||||
import { Footer } from '../../components/Footer/Footer'
|
import { Footer } from '../../components/Footer/Footer'
|
||||||
import { launch as launchNostrLoginDialog } from 'nostr-login'
|
import { launch as launchNostrLoginDialog } from 'nostr-login'
|
||||||
|
import { useDidMount } from '../../hooks'
|
||||||
|
|
||||||
export const LandingPage = () => {
|
export const LandingPage = () => {
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
const onSignInClick = async () => {
|
const onSignInClick = async () => {
|
||||||
launchNostrLoginDialog()
|
launchNostrLoginDialog()
|
||||||
}
|
}
|
||||||
|
useDidMount(() => {
|
||||||
|
const isCallback = window.location.hash.startsWith('#/?callbackPath=')
|
||||||
|
// Open nostr login if detect callback
|
||||||
|
if (isCallback) {
|
||||||
|
onSignInClick()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
@ -101,10 +105,6 @@ export const LandingPage = () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
saveVisitedLink(location.pathname, location.search)
|
|
||||||
}, [location])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.background}>
|
<div className={styles.background}>
|
||||||
<div
|
<div
|
||||||
|
21
src/routes/PrivateRoute.tsx
Normal file
21
src/routes/PrivateRoute.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
|
import { useAppSelector } from '../hooks'
|
||||||
|
import { appPublicRoutes } from '.'
|
||||||
|
|
||||||
|
export function PrivateRoute({ children }: { children: JSX.Element }) {
|
||||||
|
const location = useLocation()
|
||||||
|
const isLoggedIn = useAppSelector((state) => state.auth?.loggedIn)
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={{
|
||||||
|
pathname: appPublicRoutes.landingPage,
|
||||||
|
search: `?callbackPath=${btoa(location.pathname)}`
|
||||||
|
}}
|
||||||
|
replace
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
@ -11,6 +11,7 @@ import { RelaysPage } from '../pages/settings/relays'
|
|||||||
import { SettingsPage } from '../pages/settings/Settings'
|
import { SettingsPage } from '../pages/settings/Settings'
|
||||||
import { SignPage } from '../pages/sign'
|
import { SignPage } from '../pages/sign'
|
||||||
import { VerifyPage } from '../pages/verify'
|
import { VerifyPage } from '../pages/verify'
|
||||||
|
import { PrivateRoute } from './PrivateRoute'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper type allows for extending react-router-dom's **RouteProps** with generic type
|
* Helper type allows for extending react-router-dom's **RouteProps** with generic type
|
||||||
@ -70,34 +71,66 @@ export const publicRoutes: PublicRouteProps[] = [
|
|||||||
export const privateRoutes = [
|
export const privateRoutes = [
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.homePage,
|
path: appPrivateRoutes.homePage,
|
||||||
element: <HomePage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<HomePage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.create,
|
path: appPrivateRoutes.create,
|
||||||
element: <CreatePage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<CreatePage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${appPrivateRoutes.sign}/:id?`,
|
path: `${appPrivateRoutes.sign}/:id?`,
|
||||||
element: <SignPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<SignPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.settings,
|
path: appPrivateRoutes.settings,
|
||||||
element: <SettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<SettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.profileSettings,
|
path: appPrivateRoutes.profileSettings,
|
||||||
element: <ProfileSettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<ProfileSettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.cacheSettings,
|
path: appPrivateRoutes.cacheSettings,
|
||||||
element: <CacheSettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<CacheSettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.relays,
|
path: appPrivateRoutes.relays,
|
||||||
element: <RelaysPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<RelaysPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.nostrLogin,
|
path: appPrivateRoutes.nostrLogin,
|
||||||
element: <NostrLoginPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<NostrLoginPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -26,30 +26,6 @@ export const clearState = () => {
|
|||||||
localStorage.removeItem('state')
|
localStorage.removeItem('state')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveVisitedLink = (pathname: string, search: string) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'visitedLink',
|
|
||||||
JSON.stringify({
|
|
||||||
pathname,
|
|
||||||
search
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getVisitedLink = () => {
|
|
||||||
const visitedLink = localStorage.getItem('visitedLink')
|
|
||||||
if (!visitedLink) return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(visitedLink) as {
|
|
||||||
pathname: string
|
|
||||||
search: string
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const saveAuthToken = (token: string) => {
|
export const saveAuthToken = (token: string) => {
|
||||||
localStorage.setItem('authToken', token)
|
localStorage.setItem('authToken', token)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user