Compare commits
2 Commits
696b457978
...
49dca65470
| Author | SHA1 | Date | |
|---|---|---|---|
| 49dca65470 | |||
| 431dd106c9 |
@ -51,6 +51,7 @@
|
|||||||
"keytar": "^7.9.0",
|
"keytar": "^7.9.0",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"loglevel": "^1.9.2",
|
"loglevel": "^1.9.2",
|
||||||
|
"nanoid": "^5.1.14",
|
||||||
"online-3d-viewer": "^0.16.0",
|
"online-3d-viewer": "^0.16.0",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -134,6 +134,9 @@ importers:
|
|||||||
loglevel:
|
loglevel:
|
||||||
specifier: ^1.9.2
|
specifier: ^1.9.2
|
||||||
version: 1.9.2
|
version: 1.9.2
|
||||||
|
nanoid:
|
||||||
|
specifier: ^5.1.14
|
||||||
|
version: 5.1.14
|
||||||
online-3d-viewer:
|
online-3d-viewer:
|
||||||
specifier: ^0.16.0
|
specifier: ^0.16.0
|
||||||
version: 0.16.0
|
version: 0.16.0
|
||||||
@ -4718,6 +4721,11 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
nanoid@5.1.14:
|
||||||
|
resolution: {integrity: sha512-5c8l8kVzqpnDPaicbEop/fV0Q1w16FmbWtVhMqugTozAwYdlIQojWH5a/M7UfziFmGdQRrUdV+EPzc9Xng3VAQ==}
|
||||||
|
engines: {node: ^18 || >=20}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
nanopop@2.3.0:
|
nanopop@2.3.0:
|
||||||
resolution: {integrity: sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==}
|
resolution: {integrity: sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==}
|
||||||
|
|
||||||
@ -12095,6 +12103,8 @@ snapshots:
|
|||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
|
nanoid@5.1.14: {}
|
||||||
|
|
||||||
nanopop@2.3.0: {}
|
nanopop@2.3.0: {}
|
||||||
|
|
||||||
napi-build-utils@2.0.0: {}
|
napi-build-utils@2.0.0: {}
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import {
|
|||||||
createWindow,
|
createWindow,
|
||||||
setupMainWindowIPC,
|
setupMainWindowIPC,
|
||||||
setupMainWindowAppEvents,
|
setupMainWindowAppEvents,
|
||||||
setupDevAuthServer
|
setupDevAuthServer,
|
||||||
|
setupSingleInstanceLock,
|
||||||
|
handleDeepLinkFromArgv
|
||||||
} from './mainWindow.js'
|
} from './mainWindow.js'
|
||||||
|
|
||||||
// --- Keytar-backed auth session storage (main process) ---
|
// --- Keytar-backed auth session storage (main process) ---
|
||||||
@ -27,14 +29,19 @@ try {
|
|||||||
const KEYTAR_SERVICE = app.name || 'Farm Control'
|
const KEYTAR_SERVICE = app.name || 'Farm Control'
|
||||||
const KEYTAR_ACCOUNT = 'authSession'
|
const KEYTAR_ACCOUNT = 'authSession'
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
const gotTheLock = setupSingleInstanceLock(app)
|
||||||
createWindow()
|
|
||||||
registerGlobalShortcuts()
|
if (gotTheLock) {
|
||||||
setupSpotlightIPC()
|
app.whenReady().then(() => {
|
||||||
setupMainWindowIPC()
|
createWindow()
|
||||||
setupMainWindowAppEvents(app)
|
registerGlobalShortcuts()
|
||||||
setupDevAuthServer()
|
setupSpotlightIPC()
|
||||||
})
|
setupMainWindowIPC()
|
||||||
|
setupMainWindowAppEvents(app)
|
||||||
|
setupDevAuthServer()
|
||||||
|
handleDeepLinkFromArgv()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
app.on('will-quit', () => {
|
app.on('will-quit', () => {
|
||||||
globalShortcut.unregisterAll()
|
globalShortcut.unregisterAll()
|
||||||
|
|||||||
@ -7,6 +7,72 @@ const __dirname = dirname(__filename)
|
|||||||
|
|
||||||
let win
|
let win
|
||||||
|
|
||||||
|
const PROTOCOL_PREFIX = 'farmcontrol://'
|
||||||
|
|
||||||
|
function findProtocolUrl(args) {
|
||||||
|
return args.find(
|
||||||
|
(arg) => typeof arg === 'string' && arg.startsWith(PROTOCOL_PREFIX)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendNavigateToRenderer(redirectPath) {
|
||||||
|
const deliver = () => {
|
||||||
|
win.webContents.send('navigate', redirectPath)
|
||||||
|
win.show()
|
||||||
|
win.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!win || win.isDestroyed()) {
|
||||||
|
createWindow()
|
||||||
|
win.webContents.once('did-finish-load', () => {
|
||||||
|
setTimeout(deliver, 100)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win.webContents.isLoading()) {
|
||||||
|
win.webContents.once('did-finish-load', () => {
|
||||||
|
setTimeout(deliver, 100)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deliver()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleDeepLink(url) {
|
||||||
|
if (!url?.startsWith(`${PROTOCOL_PREFIX}app`)) return
|
||||||
|
const redirectPath = url.replace(`${PROTOCOL_PREFIX}app`, '') || '/'
|
||||||
|
sendNavigateToRenderer(redirectPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleDeepLinkFromArgv() {
|
||||||
|
if (process.platform === 'darwin') return
|
||||||
|
const url = findProtocolUrl(process.argv)
|
||||||
|
if (url) handleDeepLink(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupSingleInstanceLock(app) {
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock()
|
||||||
|
if (!gotTheLock) {
|
||||||
|
app.quit()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('second-instance', (_event, commandLine) => {
|
||||||
|
const url = findProtocolUrl(commandLine)
|
||||||
|
if (url) {
|
||||||
|
handleDeepLink(url)
|
||||||
|
} else if (win && !win.isDestroyed()) {
|
||||||
|
if (win.isMinimized()) win.restore()
|
||||||
|
win.show()
|
||||||
|
win.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function attachKeyboardShortcuts(browserWindow) {
|
function attachKeyboardShortcuts(browserWindow) {
|
||||||
if (!browserWindow) return
|
if (!browserWindow) return
|
||||||
// Keyboard shortcuts for the main window can be added here if needed
|
// Keyboard shortcuts for the main window can be added here if needed
|
||||||
@ -155,13 +221,7 @@ export function setupMainWindowAppEvents(app) {
|
|||||||
|
|
||||||
app.on('open-url', (event, url) => {
|
app.on('open-url', (event, url) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (url.startsWith('farmcontrol://app')) {
|
handleDeepLink(url)
|
||||||
// Extract the path/query after 'farmcontrol://app'
|
|
||||||
const redirectPath = url.replace('farmcontrol://app', '') || '/'
|
|
||||||
if (win && win.webContents) {
|
|
||||||
win.webContents.send('navigate', redirectPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import { MessageProvider } from './components/Dashboard/context/MessageContext.j
|
|||||||
import AuthCallback from './components/App/AuthCallback.jsx'
|
import AuthCallback from './components/App/AuthCallback.jsx'
|
||||||
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
||||||
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
||||||
|
import AuthLaunch from './components/App/AppLaunch.jsx'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductionRoutes,
|
ProductionRoutes,
|
||||||
@ -79,6 +80,7 @@ const AppContent = () => {
|
|||||||
<SpotlightProvider>
|
<SpotlightProvider>
|
||||||
<ActionsModalProvider>
|
<ActionsModalProvider>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route path='/applaunch' element={<AuthLaunch />} />
|
||||||
<Route
|
<Route
|
||||||
path='/dashboard/electron/spotlightcontent'
|
path='/dashboard/electron/spotlightcontent'
|
||||||
element={
|
element={
|
||||||
@ -114,6 +116,7 @@ const AppContent = () => {
|
|||||||
path='/email/notification'
|
path='/email/notification'
|
||||||
element={<EmailNotificationTemplate />}
|
element={<EmailNotificationTemplate />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path='/dashboard'
|
path='/dashboard'
|
||||||
element={
|
element={
|
||||||
|
|||||||
177
src/components/App/AppLaunch.jsx
Normal file
177
src/components/App/AppLaunch.jsx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Flex, Card, Alert } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import { customAlphabet } from 'nanoid'
|
||||||
|
import AuthParticles from './AppParticles'
|
||||||
|
import FarmControlLogo from '../Logos/FarmControlLogo'
|
||||||
|
import ExclamationOctagonIcon from '../Icons/ExclamationOctagonIcon'
|
||||||
|
import CheckIcon from '../Icons/CheckIcon'
|
||||||
|
import { ApiServerContext } from '../Dashboard/context/ApiServerContext'
|
||||||
|
|
||||||
|
const createLaunchSession = customAlphabet(
|
||||||
|
'01abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
32
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthLaunch = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const hasRedirected = useRef(false)
|
||||||
|
const startTimeoutRef = useRef(null)
|
||||||
|
const pollTimeoutRef = useRef(null)
|
||||||
|
const { getAppLaunchSession } = useContext(ApiServerContext)
|
||||||
|
const [launchError, setLaunchError] = useState(false)
|
||||||
|
const [launchErrorMessage, setLaunchErrorMessage] = useState('')
|
||||||
|
const [launchSuccess, setLaunchSuccess] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false
|
||||||
|
const redirect = new URLSearchParams(location.search).get('redirect')
|
||||||
|
|
||||||
|
if (!redirect) {
|
||||||
|
setLaunchError(true)
|
||||||
|
setLaunchErrorMessage('No redirect provided!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!redirect || hasRedirected.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (cancelled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRedirected.current = true
|
||||||
|
const launchSession = createLaunchSession()
|
||||||
|
let launchCheckCount = 0
|
||||||
|
|
||||||
|
setLaunchError(false)
|
||||||
|
setLaunchErrorMessage('')
|
||||||
|
setLaunchSuccess(false)
|
||||||
|
|
||||||
|
let redirectWithLaunchSession = redirect
|
||||||
|
try {
|
||||||
|
const redirectUrl = new URL(redirect, window.location.origin)
|
||||||
|
redirectUrl.searchParams.set('launchSession', launchSession)
|
||||||
|
redirectWithLaunchSession = redirectUrl.toString()
|
||||||
|
} catch {
|
||||||
|
const hasQuery = redirect.includes('?')
|
||||||
|
const separator = hasQuery ? '&' : '?'
|
||||||
|
redirectWithLaunchSession = `${redirect}${separator}launchSession=${encodeURIComponent(
|
||||||
|
launchSession
|
||||||
|
)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = redirectWithLaunchSession
|
||||||
|
link.style.display = 'none'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
|
||||||
|
const checkLaunchSession = async () => {
|
||||||
|
launchCheckCount += 1
|
||||||
|
|
||||||
|
let launchComplete = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const launchStatus = await getAppLaunchSession(launchSession)
|
||||||
|
launchComplete = launchStatus?.complete === true
|
||||||
|
} catch {
|
||||||
|
launchComplete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (launchComplete) {
|
||||||
|
setLaunchSuccess(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (launchCheckCount >= 10) {
|
||||||
|
setLaunchError(true)
|
||||||
|
setLaunchErrorMessage('Failed to open Farm Control.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pollTimeoutRef.current = setTimeout(() => {
|
||||||
|
checkLaunchSession()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLaunchSession()
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
hasRedirected.current = false
|
||||||
|
if (startTimeoutRef.current) {
|
||||||
|
clearTimeout(startTimeoutRef.current)
|
||||||
|
}
|
||||||
|
if (pollTimeoutRef.current) {
|
||||||
|
clearTimeout(pollTimeoutRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [getAppLaunchSession, location.search])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'black'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'black',
|
||||||
|
minHeight: '100vh',
|
||||||
|
transition: 'opacity 0.5s ease-in-out',
|
||||||
|
opacity: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AuthParticles />
|
||||||
|
<Flex
|
||||||
|
align='center'
|
||||||
|
justify='center'
|
||||||
|
vertical
|
||||||
|
style={{ height: '100vh' }}
|
||||||
|
gap={'large'}
|
||||||
|
>
|
||||||
|
<Card style={{ borderRadius: 20 }}>
|
||||||
|
<Flex vertical align='center'>
|
||||||
|
<FarmControlLogo style={{ fontSize: '500px', height: '40px' }} />
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
{!launchError && !launchSuccess && (
|
||||||
|
<Alert
|
||||||
|
message='Launching Farm Control please wait...'
|
||||||
|
icon={<LoadingOutlined />}
|
||||||
|
showIcon
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{launchError && (
|
||||||
|
<Alert
|
||||||
|
message={launchErrorMessage}
|
||||||
|
icon={<ExclamationOctagonIcon />}
|
||||||
|
type='error'
|
||||||
|
showIcon
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{launchSuccess && (
|
||||||
|
<Alert
|
||||||
|
message='Launch successful! You may now close this window.'
|
||||||
|
icon={<CheckIcon />}
|
||||||
|
type='success'
|
||||||
|
showIcon
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthLaunch
|
||||||
@ -11,6 +11,7 @@ import io from 'socket.io-client'
|
|||||||
import { message, Modal, Space, Button } from 'antd'
|
import { message, Modal, Space, Button } from 'antd'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { AuthContext } from './AuthContext'
|
import { AuthContext } from './AuthContext'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
|
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
|
||||||
@ -58,6 +59,8 @@ const getObjectEndpoint = (type) =>
|
|||||||
const ApiServerContext = createContext()
|
const ApiServerContext = createContext()
|
||||||
|
|
||||||
const ApiServerProvider = ({ children }) => {
|
const ApiServerProvider = ({ children }) => {
|
||||||
|
const location = useLocation()
|
||||||
|
const navigate = useNavigate()
|
||||||
const {
|
const {
|
||||||
token,
|
token,
|
||||||
userProfile,
|
userProfile,
|
||||||
@ -77,6 +80,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
const subscribedCallbacksRef = useRef(new Map())
|
const subscribedCallbacksRef = useRef(new Map())
|
||||||
const subscribedLockCallbacksRef = useRef(new Map())
|
const subscribedLockCallbacksRef = useRef(new Map())
|
||||||
const notificationListenersRef = useRef(new Set())
|
const notificationListenersRef = useRef(new Set())
|
||||||
|
const completedLaunchSessionsRef = useRef(new Set())
|
||||||
|
|
||||||
const handleLockUpdate = useCallback(
|
const handleLockUpdate = useCallback(
|
||||||
async (lockData) => {
|
async (lockData) => {
|
||||||
@ -1606,6 +1610,72 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
}, [token])
|
}, [token])
|
||||||
|
|
||||||
|
const completeAppLaunchSession = useCallback(
|
||||||
|
async (launchSession) => {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${config.backendUrl}/applaunch/${launchSession}`,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
[token]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const launchSession = new URLSearchParams(location.search).get(
|
||||||
|
'launchSession'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
authenticated !== true ||
|
||||||
|
!token ||
|
||||||
|
!launchSession ||
|
||||||
|
completedLaunchSessionsRef.current.has(launchSession)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
completedLaunchSessionsRef.current.add(launchSession)
|
||||||
|
|
||||||
|
completeAppLaunchSession(launchSession)
|
||||||
|
.then(() => {
|
||||||
|
const searchParams = new URLSearchParams(location.search)
|
||||||
|
searchParams.delete('launchSession')
|
||||||
|
const newSearch = searchParams.toString()
|
||||||
|
const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
|
||||||
|
navigate(newPath, { replace: true })
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
logger.error('Failed to complete app launch session:', err)
|
||||||
|
completedLaunchSessionsRef.current.delete(launchSession)
|
||||||
|
})
|
||||||
|
}, [
|
||||||
|
token,
|
||||||
|
authenticated,
|
||||||
|
completeAppLaunchSession,
|
||||||
|
location.search,
|
||||||
|
location.pathname,
|
||||||
|
navigate
|
||||||
|
])
|
||||||
|
|
||||||
|
const getAppLaunchSession = useCallback(async (launchSession) => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.backendUrl}/applaunch/${launchSession}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return response.data
|
||||||
|
}, [])
|
||||||
|
|
||||||
const flushFile = async (id) => {
|
const flushFile = async (id) => {
|
||||||
logger.debug('Flushing file...')
|
logger.debug('Flushing file...')
|
||||||
try {
|
try {
|
||||||
@ -1700,7 +1770,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
registerNotificationListener,
|
registerNotificationListener,
|
||||||
unregisterNotificationListener,
|
unregisterNotificationListener,
|
||||||
getMarketplaceAuthUrl,
|
getMarketplaceAuthUrl,
|
||||||
refreshMarketplaceAuth
|
refreshMarketplaceAuth,
|
||||||
|
completeAppLaunchSession,
|
||||||
|
getAppLaunchSession
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -227,6 +227,10 @@ const AuthProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location.pathname === '/applaunch') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
load()
|
load()
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user